@sleep2agi/agent-network-dashboard 0.4.6-preview.12 → 0.4.6-preview.121

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 (327) 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 +69 -55
  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/0.hnfdve7~422.js +7 -0
  181. package/.next/static/chunks/001t1axy5kvz8.css +2 -0
  182. package/.next/static/chunks/01lmbsd37fybu.js +1 -0
  183. package/.next/static/chunks/03o.h6kvmw4l_.js +1 -0
  184. package/.next/static/chunks/03~~oirxz7~vc.js +1 -0
  185. package/.next/static/chunks/05uk96gc~9mni.js +1 -0
  186. package/.next/static/chunks/08kf-.bgs10ze.js +1 -0
  187. package/.next/static/chunks/0a-6zp8m0ux5..js +1 -0
  188. package/.next/static/chunks/0ekib0be3t9i5.js +1 -0
  189. package/.next/static/chunks/0glmrdj9112tc.js +7 -0
  190. package/.next/static/chunks/0rxrez_cjn.eh.js +1 -0
  191. package/.next/static/chunks/0tvn2l1pc.h65.js +1 -0
  192. package/.next/static/chunks/0x8x.29hkju5a.js +1 -0
  193. package/.next/static/chunks/106~a5kttxze-.js +4 -0
  194. package/.next/static/chunks/174re5ar~kxne.js +1 -0
  195. package/.next/trace +2 -2
  196. package/.next/trace-build +1 -1
  197. package/.next/types/routes.d.ts +2 -1
  198. package/.next/types/validator.ts +9 -0
  199. package/app/api/hub/servers/route.ts +39 -0
  200. package/app/components/AppShell.tsx +2 -0
  201. package/app/components/ChatPopover.tsx +38 -2
  202. package/app/components/CommandPalette.tsx +94 -0
  203. package/app/components/HelpOverlay.tsx +76 -0
  204. package/app/components/ServersDrawer.tsx +206 -0
  205. package/app/components/TopoGraph.tsx +2181 -163
  206. package/app/components/utils.ts +8 -7
  207. package/app/globals.css +19 -1
  208. package/app/lib/time.ts +36 -0
  209. package/package.json +1 -1
  210. package/scripts/help-overlay-topo-test.mjs +44 -0
  211. package/scripts/servers-drawer-live-test.mjs +96 -0
  212. package/scripts/topo-active-filter-pill-test.mjs +101 -0
  213. package/scripts/topo-active-links-chip-hover-test.mjs +103 -0
  214. package/scripts/topo-active-links-chip-test.mjs +73 -0
  215. package/scripts/topo-active-links-tooltip-test.mjs +79 -0
  216. package/scripts/topo-arrival-ping-test.mjs +87 -0
  217. package/scripts/topo-arrow-tier-test.mjs +61 -0
  218. package/scripts/topo-auto-fit-test.mjs +73 -0
  219. package/scripts/topo-chat-focus-ring-test.mjs +77 -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-layout-view-test.mjs +108 -0
  223. package/scripts/topo-cmdk-pin-test.mjs +123 -0
  224. package/scripts/topo-cmdk-vendor-clear-test.mjs +133 -0
  225. package/scripts/topo-cmdk-vendor-pin-test.mjs +103 -0
  226. package/scripts/topo-cwd-tooltip-test.mjs +64 -0
  227. package/scripts/topo-dblclick-reset-test.mjs +74 -0
  228. package/scripts/topo-dispatch-pulse-test.mjs +83 -0
  229. package/scripts/topo-edge-count-badge-test.mjs +84 -0
  230. package/scripts/topo-edge-endpoint-highlight-test.mjs +111 -0
  231. package/scripts/topo-edge-focus-test.mjs +113 -0
  232. package/scripts/topo-edge-hover-test.mjs +93 -0
  233. package/scripts/topo-edge-tooltip-test.mjs +70 -0
  234. package/scripts/topo-endpoint-ring-test.mjs +93 -0
  235. package/scripts/topo-esc-clears-pin-test.mjs +98 -0
  236. package/scripts/topo-filter-pill-aliases-test.mjs +121 -0
  237. package/scripts/topo-filter-pill-body-click-test.mjs +112 -0
  238. package/scripts/topo-filter-pill-count-test.mjs +110 -0
  239. package/scripts/topo-filter-pill-mobile-test.mjs +90 -0
  240. package/scripts/topo-fit-key-test.mjs +106 -0
  241. package/scripts/topo-freshness-chip-test.mjs +74 -0
  242. package/scripts/topo-group-box-pinned-state-test.mjs +95 -0
  243. package/scripts/topo-group-count-test.mjs +68 -0
  244. package/scripts/topo-group-hover-test.mjs +82 -0
  245. package/scripts/topo-group-label-hover-test.mjs +119 -0
  246. package/scripts/topo-group-label-tint-test.mjs +99 -0
  247. package/scripts/topo-group-label-title-test.mjs +83 -0
  248. package/scripts/topo-group-pin-test.mjs +119 -0
  249. package/scripts/topo-group-status-pips-test.mjs +103 -0
  250. package/scripts/topo-groupbox-live-test.mjs +100 -0
  251. package/scripts/topo-groupbox-test.mjs +4 -2
  252. package/scripts/topo-help-overlay-r109-test.mjs +67 -0
  253. package/scripts/topo-help-overlay-r59-test.mjs +67 -0
  254. package/scripts/topo-help-overlay-r65-test.mjs +64 -0
  255. package/scripts/topo-help-overlay-r70-test.mjs +63 -0
  256. package/scripts/topo-help-overlay-r78-test.mjs +59 -0
  257. package/scripts/topo-help-overlay-r81-test.mjs +58 -0
  258. package/scripts/topo-help-overlay-r87-test.mjs +64 -0
  259. package/scripts/topo-help-overlay-r91-test.mjs +65 -0
  260. package/scripts/topo-hub-busyness-test.mjs +100 -0
  261. package/scripts/topo-hub-click-fit-test.mjs +137 -0
  262. package/scripts/topo-hub-title-test.mjs +93 -0
  263. package/scripts/topo-ingroup-edge-test.mjs +103 -0
  264. package/scripts/topo-label-lift-test.mjs +119 -0
  265. package/scripts/topo-label-scale-test.mjs +78 -0
  266. package/scripts/topo-lastseen-tooltip-test.mjs +59 -0
  267. package/scripts/topo-layout-key-test.mjs +91 -0
  268. package/scripts/topo-legend-count-test.mjs +88 -0
  269. package/scripts/topo-legend-hover-test.mjs +113 -0
  270. package/scripts/topo-legend-panel-header-test.mjs +83 -0
  271. package/scripts/topo-legend-pin-test.mjs +128 -0
  272. package/scripts/topo-legend-row-tint-test.mjs +95 -0
  273. package/scripts/topo-minimap-test.mjs +68 -0
  274. package/scripts/topo-mobile-chips-test.mjs +103 -0
  275. package/scripts/topo-node-hover-lift-test.mjs +85 -0
  276. package/scripts/topo-node-title-test.mjs +101 -0
  277. package/scripts/topo-offline-dim-test.mjs +70 -0
  278. package/scripts/topo-overlap-test.mjs +32 -5
  279. package/scripts/topo-pan-cursor-test.mjs +62 -0
  280. package/scripts/topo-panel-elevation-test.mjs +76 -0
  281. package/scripts/topo-particle-stagger-test.mjs +82 -0
  282. package/scripts/topo-pin-persistence-test.mjs +105 -0
  283. package/scripts/topo-popover-meta-test.mjs +78 -0
  284. package/scripts/topo-pressure-bar-test.mjs +69 -0
  285. package/scripts/topo-pressure-hover-test.mjs +123 -0
  286. package/scripts/topo-pressure-pin-test.mjs +119 -0
  287. package/scripts/topo-pressure-seg-tooltip-test.mjs +98 -0
  288. package/scripts/topo-pulse-rate-test.mjs +71 -0
  289. package/scripts/topo-recent-panel-count-test.mjs +72 -0
  290. package/scripts/topo-recent-row-hover-test.mjs +122 -0
  291. package/scripts/topo-recent-row-tint-test.mjs +91 -0
  292. package/scripts/topo-recent-row-ts-test.mjs +92 -0
  293. package/scripts/topo-recent-signal-empty-hint-test.mjs +73 -0
  294. package/scripts/topo-recent-signal-empty-test.mjs +63 -0
  295. package/scripts/topo-reduced-motion-test.mjs +77 -0
  296. package/scripts/topo-status-chip-hover-test.mjs +101 -0
  297. package/scripts/topo-status-chip-tooltip-test.mjs +95 -0
  298. package/scripts/topo-tier-ring-occupancy-test.mjs +87 -0
  299. package/scripts/topo-tier-ring-test.mjs +69 -0
  300. package/scripts/topo-tier-ring-tint-test.mjs +110 -0
  301. package/scripts/topo-tier-stagger-test.mjs +89 -0
  302. package/scripts/topo-tz-ghost-test.mjs +69 -0
  303. package/scripts/topo-vendor-chip-hover-test.mjs +111 -0
  304. package/scripts/topo-vendor-letter-tooltip-test.mjs +92 -0
  305. package/scripts/topo-vendor-pill-test.mjs +125 -0
  306. package/scripts/topo-vendor-pin-test.mjs +125 -0
  307. package/scripts/topo-wheel-zoom-test.mjs +79 -0
  308. package/scripts/topo-working-halo-breath-test.mjs +86 -0
  309. package/scripts/topo-zoom-keys-test.mjs +89 -0
  310. package/.next/static/chunks/0-7iowzu4.798.js +0 -1
  311. package/.next/static/chunks/02t5jr4shh.hy.js +0 -1
  312. package/.next/static/chunks/0_qfg5s5uzs7..js +0 -7
  313. package/.next/static/chunks/0cj5qr_a8dzky.js +0 -7
  314. package/.next/static/chunks/0dttftqqehpdj.js +0 -1
  315. package/.next/static/chunks/0elalb9n_.~di.js +0 -1
  316. package/.next/static/chunks/0j_q__o8p6g83.js +0 -1
  317. package/.next/static/chunks/0ja71r81z59di.js +0 -1
  318. package/.next/static/chunks/0jl6_w50zh4l5.js +0 -1
  319. package/.next/static/chunks/0r4q73.-1bkyl.js +0 -1
  320. package/.next/static/chunks/0r_c8eccg3z2o.js +0 -1
  321. package/.next/static/chunks/0z6mgaoe8c89-.js +0 -1
  322. package/.next/static/chunks/10oi7l-tycgl9.js +0 -1
  323. package/.next/static/chunks/11uk1izuv3~0t.js +0 -1
  324. package/.next/static/chunks/11y8xsp9mn.m8.css +0 -2
  325. /package/.next/static/{irozqkI_mD2Ls10yQ_4yG → fh5pUqgTFNt162QarYePi}/_buildManifest.js +0 -0
  326. /package/.next/static/{irozqkI_mD2Ls10yQ_4yG → fh5pUqgTFNt162QarYePi}/_clientMiddlewareManifest.js +0 -0
  327. /package/.next/static/{irozqkI_mD2Ls10yQ_4yG → fh5pUqgTFNt162QarYePi}/_ssgManifest.js +0 -0
@@ -0,0 +1,98 @@
1
+ /* Round 102 verification: pressure-bar segment tooltips list the
2
+ * aliases that fall into that status bucket. Closes the last "how
3
+ * many but not which" gap in the chip-row surfaces. Composes with
4
+ * the existing R83 hover preview (canvas dims still answer it
5
+ * visually too) — this adds a screen-reader-friendly textual
6
+ * channel for the same info.
7
+ *
8
+ * Fleet: 2 working + 2 idle + 1 offline. Verify each segment's
9
+ * title contains the matching aliases + action hint; data-pressure-
10
+ * seg-aliases attr carries the full CSV.
11
+ */
12
+ import { chromium } from 'playwright';
13
+ import { readFileSync } from 'node:fs';
14
+
15
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
16
+ const browser = await chromium.launch({ headless: true });
17
+
18
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
19
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
20
+ await ctx.addInitScript(() => {
21
+ try {
22
+ localStorage.setItem('anet-theme', 'cyber');
23
+ sessionStorage.setItem('anet_v3_auth', '1');
24
+ sessionStorage.removeItem('anet-topo-pinned-status');
25
+ } catch {}
26
+ });
27
+
28
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
29
+ const stale10 = new Date(Date.now() - 10 * 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 mk = (alias, status, ts) => ({
35
+ alias, status, model: 'claude-opus-4', runtime: 'cli-claude-code',
36
+ network_id: nid, project_dir: null,
37
+ created_at: ts, updated_at: ts, last_seen_at: ts,
38
+ });
39
+ await route.fulfill({ response: r, json: { ...b, sessions: [
40
+ mk('wkr1', 'working', fresh),
41
+ mk('wkr2', 'working', fresh),
42
+ mk('idl1', 'idle', fresh),
43
+ mk('idl2', 'idle', fresh),
44
+ mk('off1', 'offline', stale10),
45
+ ] } });
46
+ });
47
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
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 === 5, { timeout: 30000 });
52
+ await page.waitForTimeout(400);
53
+
54
+ const read = (key) => page.evaluate((k) => {
55
+ const el = document.querySelector(`[data-pressure-seg="${k}"]`);
56
+ if (!el) return null;
57
+ return {
58
+ title: el.getAttribute('title') || '',
59
+ aliases: el.getAttribute('data-pressure-seg-aliases') || '',
60
+ };
61
+ }, key);
62
+
63
+ const working = await read('working');
64
+ const idle = await read('idle');
65
+ const offline = await read('offline');
66
+
67
+ // Pin working — action hint should flip in the title.
68
+ await page.locator('[data-pressure-seg="working"]').click();
69
+ await page.waitForTimeout(250);
70
+ const workingPinned = await read('working');
71
+
72
+ await browser.close();
73
+
74
+ const sameSet = (csv, expected) => {
75
+ const got = new Set(csv.split(',').filter(Boolean));
76
+ const exp = new Set(expected);
77
+ return got.size === exp.size && [...exp].every(x => got.has(x));
78
+ };
79
+
80
+ const results = {
81
+ working_aliasesMatch: sameSet(working?.aliases || '', ['wkr1', 'wkr2']),
82
+ working_titleListsThem: /wkr1.*wkr2|wkr2.*wkr1/.test(working?.title || ''),
83
+ working_titleClickHint: /click to highlight/.test(working?.title || ''),
84
+ working_titleStatusN: /^2 working/m.test(working?.title || ''),
85
+ idle_aliasesMatch: sameSet(idle?.aliases || '', ['idl1', 'idl2']),
86
+ idle_titleListsThem: /idl1.*idl2|idl2.*idl1/.test(idle?.title || ''),
87
+ offline_aliasesMatch: sameSet(offline?.aliases || '', ['off1']),
88
+ offline_titleListsThem: /off1/.test(offline?.title || ''),
89
+ workingPin_aliasesStable: sameSet(workingPinned?.aliases || '', ['wkr1', 'wkr2']),
90
+ workingPin_hintFlipped: /click to release filter/.test(workingPinned?.title || ''),
91
+ };
92
+ const ok = Object.values(results).every(Boolean);
93
+ console.log(`${ok ? '✅' : '❌'} pressure-seg tooltip:`, JSON.stringify(results),
94
+ `\n working:`, working,
95
+ `\n idle:`, idle,
96
+ `\n offline:`, offline,
97
+ `\n workingPinned:`, workingPinned);
98
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,71 @@
1
+ /* Round 24 verification: working-node pulse duration tiers by sse:N.
2
+ * sse 0-1 → 1.2s (calm)
3
+ * sse 2-3 → 0.9s (active)
4
+ * sse ≥ 4 → 0.7s (busy)
5
+ * Inject sse counts via mocked /api/hub/health → sse_sessions map. */
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
+ // Three working sessions with different sse counts. Use a known network_id
23
+ // so the `${nid}:${alias}` sse-map key works (the page's grouped lookup).
24
+ const NID = 'net_test';
25
+ await ctx.route('**/api/hub/status*', async (route) => {
26
+ await route.fulfill({ json: {
27
+ ok: true,
28
+ sessions: ['calm', 'active', 'busy'].map(alias => ({
29
+ alias, status: 'working', network_id: NID, project_dir: null,
30
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
31
+ })),
32
+ } });
33
+ });
34
+ await ctx.route('**/api/hub/health*', async (route) => {
35
+ await route.fulfill({ json: {
36
+ ok: true,
37
+ version: '0.8.0-test',
38
+ sse_sessions: {
39
+ [`${NID}:calm`]: 1,
40
+ [`${NID}:active`]: 2,
41
+ [`${NID}:busy`]: 4,
42
+ },
43
+ } });
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(() => {
49
+ const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
50
+ return !!svg && svg.querySelectorAll('g[data-node]').length === 3;
51
+ }, { timeout: 30000 }).catch(() => {});
52
+ await page.waitForTimeout(600);
53
+
54
+ const durOf = alias => page.$eval(
55
+ `g[data-node="${alias}"] circle[data-pulse-dur]`,
56
+ el => el.getAttribute('data-pulse-dur'),
57
+ ).catch(() => null);
58
+
59
+ const calm = await durOf('calm');
60
+ const active = await durOf('active');
61
+ const busy = await durOf('busy');
62
+
63
+ await browser.close();
64
+ const results = {
65
+ calmIs1_2s: calm === '1.2s',
66
+ activeIs0_9s: active === '0.9s',
67
+ busyIs0_7s: busy === '0.7s',
68
+ };
69
+ const ok = Object.values(results).every(Boolean);
70
+ console.log(`${ok ? '✅' : '❌'} pulse rate:`, JSON.stringify(results), `calm=${calm} active=${active} busy=${busy}`);
71
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,72 @@
1
+ /* Round 96 verification: the recent-signal panel header count now
2
+ * reads "X flows" off flowLinks.length (matching the deduped rows
3
+ * below), not "X msgs" off the raw messages.length. Pre-R96 a
4
+ * fleet with 10 messages aggregating to 3 unique pairs read
5
+ * "10 msgs" above only 3 rendered rows — read as broken.
6
+ *
7
+ * Mock 5 messages between 2 distinct pairs: alpha↔beta gets 4
8
+ * msgs (4 separate exchanges, dedupes to 1 flow), gamma→delta
9
+ * gets 1 msg (1 flow). Total = 5 msgs, 2 flows. Header should
10
+ * show "2 flows", panel renders 2 rows.
11
+ */
12
+ import { chromium } from 'playwright';
13
+ import { readFileSync } from 'node:fs';
14
+
15
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
16
+ const browser = await chromium.launch({ headless: true });
17
+
18
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
19
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
20
+ await ctx.addInitScript(() => {
21
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
22
+ });
23
+
24
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
25
+ await ctx.route('**/api/hub/status*', async (route) => {
26
+ const r = await route.fetch();
27
+ const b = await r.json();
28
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
29
+ const mk = (alias) => ({
30
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
31
+ network_id: nid, project_dir: null,
32
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
33
+ });
34
+ await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta'), mk('gamma'), mk('delta')] } });
35
+ });
36
+
37
+ const now = Date.now();
38
+ const mkMsg = (id, from_alias, to_alias, content, ageMs) => ({
39
+ id, from_alias, to_alias, content, network_id: 'default',
40
+ created_at: new Date(now - ageMs).toISOString(),
41
+ });
42
+ // 5 messages → 2 flow pairs (alpha→beta × 4, gamma→delta × 1).
43
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
44
+ mkMsg('m1', 'alpha', 'beta', 'ping-1', 60 * 1000),
45
+ mkMsg('m2', 'alpha', 'beta', 'ping-2', 50 * 1000),
46
+ mkMsg('m3', 'alpha', 'beta', 'ping-3', 40 * 1000),
47
+ mkMsg('m4', 'alpha', 'beta', 'ping-4', 30 * 1000),
48
+ mkMsg('m5', 'gamma', 'delta', 'hello', 20 * 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 === 4, { timeout: 30000 });
54
+ await page.waitForSelector('[data-recent-panel-count]', { timeout: 10000 });
55
+ await page.waitForTimeout(400);
56
+
57
+ const headerCount = await page.locator('[data-recent-panel-count]').textContent();
58
+ const renderedRows = await page.locator('[data-recent-row]').count();
59
+
60
+ await browser.close();
61
+
62
+ const results = {
63
+ header_saysFlowsNotMsgs: /\bflows\b/.test(headerCount || ''),
64
+ header_count2: /^2 flows/.test(headerCount || ''),
65
+ rows_match_header: renderedRows === 2,
66
+ no_msgs_word: !/\bmsgs?\b/.test(headerCount || ''),
67
+ };
68
+ const ok = Object.values(results).every(Boolean);
69
+ console.log(`${ok ? '✅' : '❌'} recent-panel count:`, JSON.stringify(results),
70
+ `\n header=`, JSON.stringify(headerCount),
71
+ `\n rows=`, renderedRows);
72
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,122 @@
1
+ /* Round 56 verification: hovering a recent-signal panel row sets
2
+ * hoveredEdgeKey to that link, which fires the R50 edge-focus +
3
+ * R49 endpoint-highlight ladders against the canvas.
4
+ *
5
+ * Sessions: alpha, beta, gamma, delta, epsilon, zeta (all idle).
6
+ * Messages create three deduped flow links in this order:
7
+ * alpha → beta
8
+ * gamma → delta
9
+ * epsilon → zeta
10
+ * Hover the FIRST recent-signal row (alpha→beta):
11
+ * - alpha→beta visible edge: opacity boosts ≥1.5× baseline (R50)
12
+ * - other edges (γ→δ, ε→ζ): opacity dims < 0.6× baseline
13
+ * - alpha + beta nodes: stay bright (≥0.55) — endpoint highlight (R49)
14
+ * - gamma/delta/epsilon/zeta nodes: dim (<0.4)
15
+ * Release → all restore.
16
+ */
17
+ import { chromium } from 'playwright';
18
+ import { readFileSync } from 'node:fs';
19
+
20
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
21
+ const browser = await chromium.launch({ headless: true });
22
+
23
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
24
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
25
+ await ctx.addInitScript(() => {
26
+ try {
27
+ localStorage.setItem('anet-theme', 'cyber');
28
+ localStorage.setItem('anet-topo-layout', 'ring');
29
+ sessionStorage.setItem('anet_v3_auth', '1');
30
+ } catch {}
31
+ });
32
+
33
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
34
+ await ctx.route('**/api/hub/status*', async (route) => {
35
+ const r = await route.fetch();
36
+ const b = await r.json();
37
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
38
+ const sessions = ['alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta'].map(a => ({
39
+ alias: a, status: 'idle', network_id: nid, project_dir: null,
40
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
41
+ }));
42
+ await route.fulfill({ response: r, json: { ...b, sessions } });
43
+ });
44
+ const now = new Date().toISOString();
45
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
46
+ { from_alias: 'alpha', to_alias: 'beta', content: 'm', created_at: now },
47
+ { from_alias: 'gamma', to_alias: 'delta', content: 'm', created_at: now },
48
+ { from_alias: 'epsilon', to_alias: 'zeta', content: 'm', created_at: now },
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 === 6, { timeout: 30000 });
54
+ await page.waitForTimeout(600);
55
+
56
+ const readState = () => page.evaluate(() => {
57
+ const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
58
+ const edges = {};
59
+ for (const g of svg.querySelectorAll(':scope > g > g')) {
60
+ const t = g.querySelector('path[data-edge-hitbox] title');
61
+ if (!t) continue;
62
+ const route = (t.textContent || '').split('\n')[0];
63
+ const base = [...g.querySelectorAll(':scope > path')].find(
64
+ p => !p.hasAttribute('data-edge-hitbox') && p.hasAttribute('marker-end')
65
+ );
66
+ if (base) edges[route] = +base.getAttribute('opacity');
67
+ }
68
+ const nodes = {};
69
+ for (const a of ['alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta']) {
70
+ const n = document.querySelector(`g[data-node="${a}"]`);
71
+ nodes[a] = n ? +(n.style.opacity || '1') : null;
72
+ }
73
+ return { edges, nodes };
74
+ });
75
+
76
+ const before = await readState();
77
+
78
+ // Hover the first recent-signal row — its key is "alpha->beta".
79
+ const rowRect = await page.evaluate(() => {
80
+ const g = document.querySelector('g[data-recent-row="alpha->beta"]');
81
+ if (!g) return null;
82
+ const r = g.getBoundingClientRect();
83
+ return { x: r.x + r.width / 2, y: r.y + r.height / 2 };
84
+ });
85
+ if (!rowRect) { console.log('❌ recent-signal row alpha->beta not found'); process.exit(1); }
86
+ await page.mouse.move(10, 10);
87
+ await page.mouse.move(rowRect.x, rowRect.y);
88
+ await page.waitForTimeout(350);
89
+ const during = await readState();
90
+
91
+ await page.mouse.move(10, 10);
92
+ await page.waitForTimeout(350);
93
+ const after = await readState();
94
+
95
+ await browser.close();
96
+
97
+ const baseEdgeOp = before.edges['alpha → beta'];
98
+ const eAB = during.edges['alpha → beta'];
99
+ const eGD = during.edges['gamma → delta'];
100
+ const eEZ = during.edges['epsilon → zeta'];
101
+
102
+ const bright = (v) => v != null && v >= 0.55;
103
+ const dim = (v) => v != null && v < 0.4;
104
+ const results = {
105
+ baselineEqual: Math.abs(before.edges['alpha → beta'] - before.edges['gamma → delta']) < 0.001
106
+ && Math.abs(before.edges['alpha → beta'] - before.edges['epsilon → zeta']) < 0.001,
107
+ hoverBrightensTargetEdge: eAB > baseEdgeOp * 1.5,
108
+ hoverDimsOtherEdges: eGD < baseEdgeOp * 0.6 && eEZ < baseEdgeOp * 0.6,
109
+ endpointsStayBright: bright(during.nodes.alpha) && bright(during.nodes.beta),
110
+ nonEndpointsDim: dim(during.nodes.gamma) && dim(during.nodes.delta)
111
+ && dim(during.nodes.epsilon) && dim(during.nodes.zeta),
112
+ releaseRestoresEdges: Math.abs(after.edges['alpha → beta'] - baseEdgeOp) < 0.05
113
+ && Math.abs(after.edges['gamma → delta'] - baseEdgeOp) < 0.05,
114
+ releaseRestoresNodes: bright(after.nodes.alpha) && bright(after.nodes.gamma) && bright(after.nodes.epsilon),
115
+ };
116
+ const ok = Object.values(results).every(Boolean);
117
+ console.log(`${ok ? '✅' : '❌'} recent-row hover:`, JSON.stringify(results),
118
+ `\n baseline α→β=${baseEdgeOp}`,
119
+ `\n during α→β=${eAB} γ→δ=${eGD} ε→ζ=${eEZ}`,
120
+ `\n during nodes=`, during.nodes,
121
+ `\n after edges=`, after.edges);
122
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,91 @@
1
+ /* Round 104 verification: recent-signal panel rows get a subtle
2
+ * row-background tint on hover. R56 already brightens the matching
3
+ * edge on the canvas, but the panel row itself stayed flat — the
4
+ * row now has visible feedback at the source surface.
5
+ *
6
+ * Driver: 3 flow rows. Initial state — all hitbox rects are
7
+ * transparent. Hover row 0 — its rect fills with the accent colour
8
+ * at low alpha + data-recent-row-hovered="true"; the other two rows
9
+ * stay transparent + "false". Mouseleave — back to all-false.
10
+ */
11
+ import { chromium } from 'playwright';
12
+ import { readFileSync } from 'node:fs';
13
+
14
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
15
+ const browser = await chromium.launch({ headless: true });
16
+
17
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
18
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
19
+ await ctx.addInitScript(() => {
20
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
21
+ });
22
+
23
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
24
+ await ctx.route('**/api/hub/status*', async (route) => {
25
+ const r = await route.fetch();
26
+ const b = await r.json();
27
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
28
+ const mk = (alias) => ({
29
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
30
+ network_id: nid, project_dir: null,
31
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
32
+ });
33
+ await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta'), mk('gamma'), mk('delta')] } });
34
+ });
35
+
36
+ const now = Date.now();
37
+ const mkMsg = (id, from_alias, to_alias, ageMs) => ({
38
+ id, from_alias, to_alias, content: 'hi', network_id: 'default',
39
+ created_at: new Date(now - ageMs).toISOString(),
40
+ });
41
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
42
+ mkMsg('m1', 'alpha', 'beta', 20 * 1000),
43
+ mkMsg('m2', 'gamma', 'delta', 30 * 1000),
44
+ mkMsg('m3', 'alpha', 'gamma', 40 * 1000),
45
+ ] } }));
46
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
47
+ const page = await ctx.newPage();
48
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
49
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
50
+ await page.waitForSelector('[data-recent-row]', { timeout: 10000 });
51
+ await page.waitForTimeout(400);
52
+
53
+ const readRows = () => page.evaluate(() => {
54
+ return [...document.querySelectorAll('[data-recent-row]')].map(g => {
55
+ const rect = g.querySelector('rect');
56
+ return {
57
+ key: g.getAttribute('data-recent-row'),
58
+ hovered: g.getAttribute('data-recent-row-hovered'),
59
+ rectFill: rect?.getAttribute('fill') || '',
60
+ rectOpac: rect?.getAttribute('opacity') || '',
61
+ };
62
+ });
63
+ });
64
+
65
+ const before = await readRows();
66
+
67
+ // Hover the first row.
68
+ await page.locator('[data-recent-row]').first().hover();
69
+ await page.waitForTimeout(250);
70
+ const duringHover = await readRows();
71
+
72
+ // Move pointer away.
73
+ await page.mouse.move(10, 10);
74
+ await page.waitForTimeout(250);
75
+ const afterLeave = await readRows();
76
+
77
+ await browser.close();
78
+
79
+ const results = {
80
+ before_allTransparent: before.every(r => r.rectFill === 'transparent' && r.hovered === 'false'),
81
+ hover_firstRowTinted: duringHover[0]?.hovered === 'true' && duringHover[0]?.rectFill !== 'transparent',
82
+ hover_otherRowsUnchanged: duringHover.slice(1).every(r => r.hovered === 'false' && r.rectFill === 'transparent'),
83
+ hover_opacityVisibleButLow: duringHover[0] && parseFloat(duringHover[0].rectOpac) > 0 && parseFloat(duringHover[0].rectOpac) < 0.3,
84
+ leave_restored: afterLeave.every(r => r.rectFill === 'transparent' && r.hovered === 'false'),
85
+ };
86
+ const ok = Object.values(results).every(Boolean);
87
+ console.log(`${ok ? '✅' : '❌'} recent-row tint:`, JSON.stringify(results),
88
+ `\n before=`, before,
89
+ `\n duringHover=`, duringHover,
90
+ `\n afterLeave=`, afterLeave);
91
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,92 @@
1
+ /* Round 94 verification: each recent-signal panel row now carries a
2
+ * right-aligned relative timestamp (e.g. `2s` / `1m`). Composes with
3
+ * the R56 hover affordance and R57 panel elevation without changing
4
+ * geometry. Mirror what the R42 active-links chip already shows for
5
+ * the most-recent flow, but per-row so users can see which flows
6
+ * are current vs stale.
7
+ *
8
+ * Drive the dashboard with messages at known ages — 30s, 5m, 2h —
9
+ * and assert each row's timestamp matches the expected glyph. Also
10
+ * verify the timestamp <text> is non-interactive (pointerEvents:none)
11
+ * so it doesn't steal hover from the row hitbox.
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 {
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 mk = (alias) => ({
35
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
36
+ network_id: nid, project_dir: null,
37
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
38
+ });
39
+ await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta'), mk('gamma'), mk('delta')] } });
40
+ });
41
+
42
+ // Mock messages so flowLinks aggregates them. Three flows at increasing
43
+ // ages — the panel sorts most-recent first (per the recent-signal
44
+ // aggregator), so row 0 is the freshest.
45
+ const now = Date.now();
46
+ const mkMsg = (id, from_alias, to_alias, content, ageMs) => ({
47
+ id, from_alias, to_alias, content, network_id: 'default',
48
+ created_at: new Date(now - ageMs).toISOString(),
49
+ });
50
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
51
+ mkMsg('m1', 'alpha', 'beta', 'fix-the-bug', 30 * 1000), // 30s ago → "30s"
52
+ mkMsg('m2', 'gamma', 'delta', 'review-the-pr', 5 * 60 * 1000), // 5m ago → "5m"
53
+ mkMsg('m3', 'alpha', 'gamma', 'deploy-stage', 2 * 60 * 60 * 1000), // 2h ago → "2h"
54
+ ] } }));
55
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
56
+ const page = await ctx.newPage();
57
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
58
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
59
+ // Wait for at least one recent-signal timestamp to render.
60
+ await page.waitForSelector('[data-recent-row-ts]', { timeout: 10000 });
61
+ await page.waitForTimeout(400);
62
+
63
+ const rows = await page.evaluate(() => {
64
+ return [...document.querySelectorAll('[data-recent-row]')].map(g => {
65
+ const rowKey = g.getAttribute('data-recent-row');
66
+ const tsEl = g.querySelector('[data-recent-row-ts]');
67
+ const rowText = g.querySelector('text:not([data-recent-row-ts])')?.textContent || '';
68
+ const tsText = tsEl?.textContent || null;
69
+ const tsPointerEvents = tsEl ? getComputedStyle(tsEl).pointerEvents : null;
70
+ return { rowKey, rowText, tsText, tsPointerEvents };
71
+ });
72
+ });
73
+
74
+ await browser.close();
75
+
76
+ const results = {
77
+ threeRows: rows.length === 3,
78
+ // Row 0 (freshest) should be alpha→beta around "30s" or "29s" etc.
79
+ row0_hasFreshTs: rows[0] && /^[2-9]\d?s$/.test(rows[0].tsText || ''),
80
+ // Row 1 should read 5m (minutes precision).
81
+ row1_minuteTs: rows[1] && /^[4-6]m$/.test(rows[1].tsText || ''),
82
+ // Row 2 should read ~2h.
83
+ row2_hourTs: rows[2] && /^[12]h$/.test(rows[2].tsText || ''),
84
+ // ts <text> must be non-interactive so it doesn't intercept the row hover.
85
+ ts_nonInteractive: rows.every(r => r.tsPointerEvents === 'none'),
86
+ // Row content shows from/to/count/content via the existing layout.
87
+ row0_hasArrow: rows[0]?.rowText.includes('->'),
88
+ };
89
+ const ok = Object.values(results).every(Boolean);
90
+ console.log(`${ok ? '✅' : '❌'} recent-row ts:`, JSON.stringify(results),
91
+ `\n rows=`, rows);
92
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,73 @@
1
+ /* Round 110 verification: recent-signal empty placeholder gains a
2
+ * sub-text hint so the empty state reads as intentional invitation
3
+ * rather than a stuck-loading state. Primary line "no flow yet"
4
+ * stays at the same position semantics (now y=54 instead of y=60
5
+ * to balance the two-line stack within the panel); sub-line at
6
+ * y=68 says "send a message between agents".
7
+ *
8
+ * Verifies:
9
+ * - both lines render when flowLinks is empty
10
+ * - both lines disappear when flowLinks has content
11
+ * - data-recent-signal-empty + data-recent-signal-empty-hint
12
+ * attributes are stable test hooks
13
+ */
14
+ import { chromium } from 'playwright';
15
+ import { readFileSync } from 'node:fs';
16
+
17
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
18
+ const browser = await chromium.launch({ headless: true });
19
+ const fresh = new Date(Date.now() - 30 * 1000).toISOString();
20
+
21
+ const probe = async (withFlow) => {
22
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1200 } });
23
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
24
+ await ctx.addInitScript(() => {
25
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
26
+ });
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')] } });
37
+ });
38
+ const now = Date.now();
39
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: withFlow ? [
40
+ { id: 'm1', from_alias: 'alpha', to_alias: 'beta', content: 'hi',
41
+ network_id: 'default', created_at: new Date(now - 10000).toISOString() },
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 === 2, { timeout: 30000 });
47
+ await page.waitForTimeout(600);
48
+ const data = await page.evaluate(() => ({
49
+ empty: document.querySelector('[data-recent-signal-empty]')?.textContent || null,
50
+ emptyHint: document.querySelector('[data-recent-signal-empty-hint]')?.textContent || null,
51
+ rowCount: document.querySelectorAll('[data-recent-row]').length,
52
+ }));
53
+ await ctx.close();
54
+ return data;
55
+ };
56
+
57
+ const empty = await probe(false);
58
+ const full = await probe(true);
59
+ await browser.close();
60
+
61
+ const results = {
62
+ empty_primaryLineShown: empty.empty === 'no flow yet',
63
+ empty_hintShown: /^send a message between agents$/.test(empty.emptyHint || ''),
64
+ empty_noRows: empty.rowCount === 0,
65
+ full_primaryGone: full.empty === null,
66
+ full_hintGone: full.emptyHint === null,
67
+ full_rowsRendered: full.rowCount >= 1,
68
+ };
69
+ const ok = Object.values(results).every(Boolean);
70
+ console.log(`${ok ? '✅' : '❌'} recent-signal empty hint:`, JSON.stringify(results),
71
+ `\n empty=`, empty,
72
+ `\n full=`, full);
73
+ process.exit(ok ? 0 : 1);