@sleep2agi/agent-network-dashboard 0.4.6-preview.41 → 0.4.6-preview.415

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 (545) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +2 -0
  3. package/.next/build-manifest.json +3 -3
  4. package/.next/diagnostics/route-bundle-stats.json +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 +16 -0
  8. package/.next/server/app/_global-error.html +1 -1
  9. package/.next/server/app/_global-error.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  13. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  14. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  15. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  16. package/.next/server/app/_not-found.html +2 -2
  17. package/.next/server/app/_not-found.rsc +12 -12
  18. package/.next/server/app/_not-found.segments/_full.segment.rsc +12 -12
  19. package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  20. package/.next/server/app/_not-found.segments/_index.segment.rsc +7 -7
  21. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  22. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  23. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  24. package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
  25. package/.next/server/app/admin.html +2 -2
  26. package/.next/server/app/admin.rsc +14 -14
  27. package/.next/server/app/admin.segments/_full.segment.rsc +14 -14
  28. package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
  29. package/.next/server/app/admin.segments/_index.segment.rsc +7 -7
  30. package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
  31. package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
  32. package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
  33. package/.next/server/app/api/hub/server/[hostname]/agents/route/app-paths-manifest.json +3 -0
  34. package/.next/server/app/api/hub/server/[hostname]/agents/route/build-manifest.json +9 -0
  35. package/.next/server/app/api/hub/server/[hostname]/agents/route/server-reference-manifest.json +4 -0
  36. package/.next/server/app/api/hub/server/[hostname]/agents/route.js +7 -0
  37. package/.next/server/app/api/hub/server/[hostname]/agents/route.js.map +5 -0
  38. package/.next/server/app/api/hub/server/[hostname]/agents/route.js.nft.json +1 -0
  39. package/.next/server/app/api/hub/server/[hostname]/agents/route_client-reference-manifest.js +3 -0
  40. package/.next/server/app/api/hub/server/[hostname]/health/route/app-paths-manifest.json +3 -0
  41. package/.next/server/app/api/hub/server/[hostname]/health/route/build-manifest.json +9 -0
  42. package/.next/server/app/api/hub/server/[hostname]/health/route/server-reference-manifest.json +4 -0
  43. package/.next/server/app/api/hub/server/[hostname]/health/route.js +7 -0
  44. package/.next/server/app/api/hub/server/[hostname]/health/route.js.map +5 -0
  45. package/.next/server/app/api/hub/server/[hostname]/health/route.js.nft.json +1 -0
  46. package/.next/server/app/api/hub/server/[hostname]/health/route_client-reference-manifest.js +3 -0
  47. package/.next/server/app/index.html +2 -2
  48. package/.next/server/app/index.rsc +14 -14
  49. package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
  50. package/.next/server/app/index.segments/_full.segment.rsc +14 -14
  51. package/.next/server/app/index.segments/_head.segment.rsc +4 -4
  52. package/.next/server/app/index.segments/_index.segment.rsc +7 -7
  53. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  54. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  55. package/.next/server/app/login.html +2 -2
  56. package/.next/server/app/login.rsc +14 -14
  57. package/.next/server/app/login.segments/_full.segment.rsc +14 -14
  58. package/.next/server/app/login.segments/_head.segment.rsc +4 -4
  59. package/.next/server/app/login.segments/_index.segment.rsc +7 -7
  60. package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
  61. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
  62. package/.next/server/app/login.segments/login.segment.rsc +3 -3
  63. package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
  64. package/.next/server/app/logs.html +2 -2
  65. package/.next/server/app/logs.rsc +14 -14
  66. package/.next/server/app/logs.segments/_full.segment.rsc +14 -14
  67. package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
  68. package/.next/server/app/logs.segments/_index.segment.rsc +7 -7
  69. package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
  70. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
  71. package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
  72. package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
  73. package/.next/server/app/messages.html +2 -2
  74. package/.next/server/app/messages.rsc +14 -14
  75. package/.next/server/app/messages.segments/_full.segment.rsc +14 -14
  76. package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
  77. package/.next/server/app/messages.segments/_index.segment.rsc +7 -7
  78. package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
  79. package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
  80. package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
  81. package/.next/server/app/node/page_client-reference-manifest.js +1 -1
  82. package/.next/server/app/node.html +2 -2
  83. package/.next/server/app/node.rsc +14 -14
  84. package/.next/server/app/node.segments/_full.segment.rsc +14 -14
  85. package/.next/server/app/node.segments/_head.segment.rsc +4 -4
  86. package/.next/server/app/node.segments/_index.segment.rsc +7 -7
  87. package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
  88. package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
  89. package/.next/server/app/node.segments/node.segment.rsc +3 -3
  90. package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
  91. package/.next/server/app/nodes.html +2 -2
  92. package/.next/server/app/nodes.rsc +14 -14
  93. package/.next/server/app/nodes.segments/_full.segment.rsc +14 -14
  94. package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
  95. package/.next/server/app/nodes.segments/_index.segment.rsc +7 -7
  96. package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
  97. package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
  98. package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
  99. package/.next/server/app/page_client-reference-manifest.js +1 -1
  100. package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
  101. package/.next/server/app/server-logs.html +2 -2
  102. package/.next/server/app/server-logs.rsc +14 -14
  103. package/.next/server/app/server-logs.segments/_full.segment.rsc +14 -14
  104. package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
  105. package/.next/server/app/server-logs.segments/_index.segment.rsc +7 -7
  106. package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
  107. package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
  108. package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
  109. package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
  110. package/.next/server/app/settings/networks.html +2 -2
  111. package/.next/server/app/settings/networks.rsc +14 -14
  112. package/.next/server/app/settings/networks.segments/_full.segment.rsc +14 -14
  113. package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
  114. package/.next/server/app/settings/networks.segments/_index.segment.rsc +7 -7
  115. package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
  116. package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
  117. package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
  118. package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
  119. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  120. package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
  121. package/.next/server/app/settings/tokens.html +2 -2
  122. package/.next/server/app/settings/tokens.rsc +14 -14
  123. package/.next/server/app/settings/tokens.segments/_full.segment.rsc +14 -14
  124. package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
  125. package/.next/server/app/settings/tokens.segments/_index.segment.rsc +7 -7
  126. package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
  127. package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
  128. package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
  129. package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
  130. package/.next/server/app/settings.html +2 -2
  131. package/.next/server/app/settings.rsc +14 -14
  132. package/.next/server/app/settings.segments/_full.segment.rsc +14 -14
  133. package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
  134. package/.next/server/app/settings.segments/_index.segment.rsc +7 -7
  135. package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  136. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
  137. package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
  138. package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
  139. package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
  140. package/.next/server/app/tasks.html +2 -2
  141. package/.next/server/app/tasks.rsc +14 -14
  142. package/.next/server/app/tasks.segments/_full.segment.rsc +14 -14
  143. package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
  144. package/.next/server/app/tasks.segments/_index.segment.rsc +7 -7
  145. package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
  146. package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
  147. package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
  148. package/.next/server/app-paths-manifest.json +2 -0
  149. package/.next/server/chunks/0dk5_next-internal_server_app_api_hub_server_[hostname]_agents_route_actions_0vr92s4.js +3 -0
  150. package/.next/server/chunks/0dk5_next-internal_server_app_api_hub_server_[hostname]_agents_route_actions_0vr92s4.js.map +1 -0
  151. package/.next/server/chunks/0dk5_next-internal_server_app_api_hub_server_[hostname]_health_route_actions_0vr-bv5.js +3 -0
  152. package/.next/server/chunks/0dk5_next-internal_server_app_api_hub_server_[hostname]_health_route_actions_0vr-bv5.js.map +1 -0
  153. package/.next/server/chunks/[root-of-the-server]__05nyivw._.js +3 -0
  154. package/.next/server/chunks/[root-of-the-server]__05nyivw._.js.map +1 -0
  155. package/.next/server/chunks/[root-of-the-server]__0floe3h._.js +3 -0
  156. package/.next/server/chunks/[root-of-the-server]__0floe3h._.js.map +1 -0
  157. package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js +1 -1
  158. package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js.map +1 -1
  159. package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js +1 -1
  160. package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js.map +1 -1
  161. package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js +1 -1
  162. package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js.map +1 -1
  163. package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js +1 -1
  164. package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js.map +1 -1
  165. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +4 -1
  166. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
  167. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
  168. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
  169. package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.js +1 -1
  170. package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.js.map +1 -1
  171. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
  172. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
  173. package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js +1 -1
  174. package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js.map +1 -1
  175. package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js +1 -1
  176. package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js.map +1 -1
  177. package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js +1 -1
  178. package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js.map +1 -1
  179. package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js +1 -1
  180. package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js.map +1 -1
  181. package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js +1 -1
  182. package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js.map +1 -1
  183. package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js +1 -1
  184. package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js.map +1 -1
  185. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js +1 -1
  186. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js.map +1 -1
  187. package/.next/server/middleware-build-manifest.js +3 -3
  188. package/.next/server/pages/404.html +2 -2
  189. package/.next/server/pages/500.html +1 -1
  190. package/.next/server/server-reference-manifest.js +1 -1
  191. package/.next/server/server-reference-manifest.json +1 -1
  192. package/.next/static/chunks/0-mpa_947ipeq.js +1 -0
  193. package/.next/static/chunks/01lmbsd37fybu.js +1 -0
  194. package/.next/static/chunks/03o.h6kvmw4l_.js +1 -0
  195. package/.next/static/chunks/03~~oirxz7~vc.js +1 -0
  196. package/.next/static/chunks/05uk96gc~9mni.js +1 -0
  197. package/.next/static/chunks/08kf-.bgs10ze.js +1 -0
  198. package/.next/static/chunks/0_cxs7bhk9fy-.js +1 -0
  199. package/.next/static/chunks/0aauz~36q5n2a.css +2 -0
  200. package/.next/static/chunks/0glmrdj9112tc.js +7 -0
  201. package/.next/static/chunks/0l_~q07bhpkcx.js +7 -0
  202. package/.next/static/chunks/0rxrez_cjn.eh.js +1 -0
  203. package/.next/static/chunks/0s7~zeuhn0t35.js +4 -0
  204. package/.next/static/chunks/0vo1q0pvypqub.js +1 -0
  205. package/.next/static/chunks/0x8x.29hkju5a.js +1 -0
  206. package/.next/static/chunks/174re5ar~kxne.js +1 -0
  207. package/.next/trace +2 -2
  208. package/.next/trace-build +1 -1
  209. package/.next/types/routes.d.ts +3 -1
  210. package/.next/types/validator.ts +18 -0
  211. package/app/api/hub/server/[hostname]/agents/route.ts +62 -0
  212. package/app/api/hub/server/[hostname]/health/route.ts +58 -0
  213. package/app/api/hub/servers/route.ts +88 -1
  214. package/app/components/ChatPopover.tsx +38 -2
  215. package/app/components/CommandPalette.tsx +107 -0
  216. package/app/components/HelpOverlay.tsx +109 -1
  217. package/app/components/ServersDrawer.tsx +181 -5
  218. package/app/components/TopoGraph.tsx +6552 -282
  219. package/app/components/utils.ts +8 -7
  220. package/app/globals.css +110 -5
  221. package/app/lib/time.ts +36 -0
  222. package/app/lib/vendorIdentity.ts +14 -3
  223. package/docs/rfc/v0.10.0-hero3-dashboard-upgrade.md +230 -0
  224. package/package.json +2 -2
  225. package/public/sleep2agi-logo.svg +11 -0
  226. package/public/vendors/claude.svg +11 -0
  227. package/public/vendors/minimax.svg +11 -0
  228. package/public/vendors/openai.svg +12 -0
  229. package/screenshots/r293-p0/after-full.png +0 -0
  230. package/screenshots/r293-p0/after-topo.png +0 -0
  231. package/screenshots/r293-p0/baseline-full.png +0 -0
  232. package/screenshots/r293-p0/baseline-topo.png +0 -0
  233. package/screenshots/r293-p0/logo-integrated-full.png +0 -0
  234. package/screenshots/r293-p0/logo-integrated-topo.png +0 -0
  235. package/screenshots/r293-p0/servers-drawer-v0.10.0-full.png +0 -0
  236. package/screenshots/r293-p0/servers-drawer-v0.10.0.png +0 -0
  237. package/screenshots/r293-p0/wave1-after-full.png +0 -0
  238. package/screenshots/r293-p0/wave1-after-topo.png +0 -0
  239. package/scripts/p0-servers-drawer-screenshot.mjs +99 -0
  240. package/scripts/p0-topo-screenshot.mjs +82 -0
  241. package/scripts/topo-active-filter-pill-test.mjs +101 -0
  242. package/scripts/topo-active-links-chip-hover-test.mjs +103 -0
  243. package/scripts/topo-active-links-chip-nav-test.mjs +117 -0
  244. package/scripts/topo-active-links-chip-self-hover-test.mjs +171 -0
  245. package/scripts/topo-active-links-chip-test.mjs +73 -0
  246. package/scripts/topo-active-links-empty-recede-test.mjs +125 -0
  247. package/scripts/topo-active-links-freshness-test.mjs +113 -0
  248. package/scripts/topo-active-links-tooltip-test.mjs +79 -0
  249. package/scripts/topo-active-pulse-ease-test.mjs +120 -0
  250. package/scripts/topo-brand-logo-transition-test.mjs +94 -0
  251. package/scripts/topo-brand-watermark-test.mjs +97 -0
  252. package/scripts/topo-canvas-aria-test.mjs +108 -0
  253. package/scripts/topo-canvas-brand-fade-test.mjs +87 -0
  254. package/scripts/topo-chat-target-breath-test.mjs +84 -0
  255. package/scripts/topo-chat-target-ring-ease-test.mjs +82 -0
  256. package/scripts/topo-chat-target-ring-fade-test.mjs +124 -0
  257. package/scripts/topo-chip-empty-recede-test.mjs +116 -0
  258. package/scripts/topo-chip-focus-test.mjs +95 -0
  259. package/scripts/topo-chip-pin-mirror-test.mjs +113 -0
  260. package/scripts/topo-chip-row-gap-test.mjs +90 -0
  261. package/scripts/topo-chip-row-semantic-gap-test.mjs +97 -0
  262. package/scripts/topo-chip-row-tabular-test.mjs +111 -0
  263. package/scripts/topo-chip-unit-demotion-test.mjs +93 -0
  264. package/scripts/topo-chiprow-data-weight-test.mjs +94 -0
  265. package/scripts/topo-chrome-a11y-test.mjs +107 -0
  266. package/scripts/topo-chrome-active-weight-test.mjs +104 -0
  267. package/scripts/topo-chrome-focus-ring-unify-test.mjs +100 -0
  268. package/scripts/topo-chrome-gap-test.mjs +73 -0
  269. package/scripts/topo-chrome-icon-stroke-test.mjs +95 -0
  270. package/scripts/topo-chrome-inactive-color-test.mjs +90 -0
  271. package/scripts/topo-chrome-pop-extension-test.mjs +98 -0
  272. package/scripts/topo-chrome-pop-nodesize-test.mjs +88 -0
  273. package/scripts/topo-chrome-press-state-test.mjs +135 -0
  274. package/scripts/topo-chrome-strip-semantic-gap-test.mjs +99 -0
  275. package/scripts/topo-chrome-strip-theme-ease-test.mjs +99 -0
  276. package/scripts/topo-chrome-zoom-level-pop-test.mjs +122 -0
  277. package/scripts/topo-chrome-zoom-pop-test.mjs +130 -0
  278. package/scripts/topo-chrome-zoom-readout-weight-test.mjs +81 -0
  279. package/scripts/topo-click-ripple-ease-test.mjs +94 -0
  280. package/scripts/topo-cmdk-edge-clear-test.mjs +109 -0
  281. package/scripts/topo-cmdk-layout-view-test.mjs +108 -0
  282. package/scripts/topo-cmdk-pin-test.mjs +123 -0
  283. package/scripts/topo-cmdk-vendor-clear-test.mjs +133 -0
  284. package/scripts/topo-cmdk-vendor-pin-test.mjs +103 -0
  285. package/scripts/topo-corner-radius-unify-test.mjs +103 -0
  286. package/scripts/topo-dblclick-reset-test.mjs +74 -0
  287. package/scripts/topo-dense-alias-fill-transition-test.mjs +133 -0
  288. package/scripts/topo-discrete-zoom-smooth-test.mjs +125 -0
  289. package/scripts/topo-edge-badge-click-pin-test.mjs +94 -0
  290. package/scripts/topo-edge-badge-crossfade-test.mjs +138 -0
  291. package/scripts/topo-edge-badge-hot-test.mjs +115 -0
  292. package/scripts/topo-edge-badge-hover-test.mjs +85 -0
  293. package/scripts/topo-edge-badge-lift-test.mjs +150 -0
  294. package/scripts/topo-edge-badge-ripple-test.mjs +111 -0
  295. package/scripts/topo-edge-badge-stroke-transition-test.mjs +118 -0
  296. package/scripts/topo-edge-badge-text-pin-test.mjs +121 -0
  297. package/scripts/topo-edge-badge-theme-ease-test.mjs +89 -0
  298. package/scripts/topo-edge-count-badge-test.mjs +86 -0
  299. package/scripts/topo-edge-endpoint-highlight-test.mjs +111 -0
  300. package/scripts/topo-edge-fadein-test.mjs +106 -0
  301. package/scripts/topo-edge-focus-test.mjs +113 -0
  302. package/scripts/topo-edge-hover-test.mjs +93 -0
  303. package/scripts/topo-edge-pill-hot-test.mjs +129 -0
  304. package/scripts/topo-edge-pill-test.mjs +105 -0
  305. package/scripts/topo-edge-ping-pulse-retired-test.mjs +119 -0
  306. package/scripts/topo-edge-stroke-ease-test.mjs +100 -0
  307. package/scripts/topo-edge-stroke-transition-test.mjs +132 -0
  308. package/scripts/topo-edge-tooltip-test.mjs +70 -0
  309. package/scripts/topo-empty-hint-tracking-test.mjs +84 -0
  310. package/scripts/topo-empty-state-breath-test.mjs +150 -0
  311. package/scripts/topo-empty-state-crossfade-test.mjs +130 -0
  312. package/scripts/topo-empty-state-recenter-test.mjs +107 -0
  313. package/scripts/topo-empty-subhint-tracking-test.mjs +87 -0
  314. package/scripts/topo-endpoint-ring-fade-test.mjs +132 -0
  315. package/scripts/topo-endpoint-ring-test.mjs +93 -0
  316. package/scripts/topo-endpoint-ring-thicken-test.mjs +112 -0
  317. package/scripts/topo-esc-clears-pin-test.mjs +98 -0
  318. package/scripts/topo-filter-pill-aliases-test.mjs +121 -0
  319. package/scripts/topo-filter-pill-body-click-test.mjs +112 -0
  320. package/scripts/topo-filter-pill-count-tabular-test.mjs +135 -0
  321. package/scripts/topo-filter-pill-count-test.mjs +110 -0
  322. package/scripts/topo-filter-pill-mobile-test.mjs +90 -0
  323. package/scripts/topo-freshness-chip-test.mjs +74 -0
  324. package/scripts/topo-freshness-chip-transition-test.mjs +80 -0
  325. package/scripts/topo-freshness-chip-weight-test.mjs +90 -0
  326. package/scripts/topo-freshness-conditional-render-test.mjs +92 -0
  327. package/scripts/topo-fullscreen-active-test.mjs +115 -0
  328. package/scripts/topo-group-box-pinned-state-test.mjs +95 -0
  329. package/scripts/topo-group-count-test.mjs +6 -1
  330. package/scripts/topo-group-label-a11y-test.mjs +113 -0
  331. package/scripts/topo-group-label-count-inherit-test.mjs +99 -0
  332. package/scripts/topo-group-label-hover-test.mjs +119 -0
  333. package/scripts/topo-group-label-pin-spacing-test.mjs +136 -0
  334. package/scripts/topo-group-label-tint-test.mjs +99 -0
  335. package/scripts/topo-group-label-title-test.mjs +83 -0
  336. package/scripts/topo-group-pin-test.mjs +119 -0
  337. package/scripts/topo-group-pip-fadein-test.mjs +104 -0
  338. package/scripts/topo-group-pip-singletier-drop-test.mjs +131 -0
  339. package/scripts/topo-group-pip-tabular-test.mjs +95 -0
  340. package/scripts/topo-group-status-pips-test.mjs +103 -0
  341. package/scripts/topo-groupbox-fadein-test.mjs +95 -0
  342. package/scripts/topo-groupbox-hover-lift-test.mjs +127 -0
  343. package/scripts/topo-groupbox-hubcore-ease-test.mjs +106 -0
  344. package/scripts/topo-groupbox-live-test.mjs +100 -0
  345. package/scripts/topo-groupbox-march-busyness-test.mjs +155 -0
  346. package/scripts/topo-groupbox-test.mjs +4 -2
  347. package/scripts/topo-header-gap-test.mjs +94 -0
  348. package/scripts/topo-help-overlay-r109-test.mjs +67 -0
  349. package/scripts/topo-help-overlay-r118-test.mjs +69 -0
  350. package/scripts/topo-help-overlay-r123-test.mjs +69 -0
  351. package/scripts/topo-help-overlay-r134-test.mjs +74 -0
  352. package/scripts/topo-help-overlay-r137-test.mjs +81 -0
  353. package/scripts/topo-help-overlay-r141-test.mjs +86 -0
  354. package/scripts/topo-help-overlay-r153-test.mjs +85 -0
  355. package/scripts/topo-help-overlay-r59-test.mjs +67 -0
  356. package/scripts/topo-help-overlay-r65-test.mjs +64 -0
  357. package/scripts/topo-help-overlay-r70-test.mjs +63 -0
  358. package/scripts/topo-help-overlay-r78-test.mjs +59 -0
  359. package/scripts/topo-help-overlay-r81-test.mjs +58 -0
  360. package/scripts/topo-help-overlay-r87-test.mjs +64 -0
  361. package/scripts/topo-help-overlay-r91-test.mjs +65 -0
  362. package/scripts/topo-hot-tail-crossfade-test.mjs +117 -0
  363. package/scripts/topo-html-overlay-padding-unify-test.mjs +120 -0
  364. package/scripts/topo-hub-a11y-test.mjs +112 -0
  365. package/scripts/topo-hub-busyness-test.mjs +100 -0
  366. package/scripts/topo-hub-center-crossfade-test.mjs +124 -0
  367. package/scripts/topo-hub-click-fit-test.mjs +137 -0
  368. package/scripts/topo-hub-digit-scale-test.mjs +129 -0
  369. package/scripts/topo-hub-fadein-test.mjs +88 -0
  370. package/scripts/topo-hub-hover-lift-test.mjs +101 -0
  371. package/scripts/topo-hub-hover-ring-test.mjs +82 -0
  372. package/scripts/topo-hub-pip-theme-ease-test.mjs +116 -0
  373. package/scripts/topo-hub-spoke-ease-test.mjs +120 -0
  374. package/scripts/topo-hub-title-test.mjs +93 -0
  375. package/scripts/topo-hub-working-count-test.mjs +99 -0
  376. package/scripts/topo-ingroup-edge-test.mjs +103 -0
  377. package/scripts/topo-inner-radar-ring-retired-test.mjs +89 -0
  378. package/scripts/topo-kicker-color-test.mjs +93 -0
  379. package/scripts/topo-kicker-tracking-test.mjs +101 -0
  380. package/scripts/topo-kicker-weight-test.mjs +98 -0
  381. package/scripts/topo-label-card-theme-ease-test.mjs +85 -0
  382. package/scripts/topo-label-lift-test.mjs +5 -1
  383. package/scripts/topo-layout-hover-text-preview-test.mjs +101 -0
  384. package/scripts/topo-layout-switch-crossfade-test.mjs +126 -0
  385. package/scripts/topo-layout-toggle-border-unify-test.mjs +104 -0
  386. package/scripts/topo-layout-toggle-test.mjs +131 -0
  387. package/scripts/topo-layout-trailer-mr-test.mjs +82 -0
  388. package/scripts/topo-legend-compress-test.mjs +107 -0
  389. package/scripts/topo-legend-count-empty-test.mjs +126 -0
  390. package/scripts/topo-legend-count-tabular-parity-test.mjs +90 -0
  391. package/scripts/topo-legend-count-tabular-test.mjs +87 -0
  392. package/scripts/topo-legend-count-test.mjs +88 -0
  393. package/scripts/topo-legend-count-tier-fill-test.mjs +121 -0
  394. package/scripts/topo-legend-count-weight-test.mjs +86 -0
  395. package/scripts/topo-legend-header-symmetric-inset-test.mjs +112 -0
  396. package/scripts/topo-legend-hitbox-swatch-center-test.mjs +93 -0
  397. package/scripts/topo-legend-hover-test.mjs +113 -0
  398. package/scripts/topo-legend-offline-delimiter-test.mjs +84 -0
  399. package/scripts/topo-legend-offline-label-simplify-test.mjs +86 -0
  400. package/scripts/topo-legend-panel-count-weight-test.mjs +89 -0
  401. package/scripts/topo-legend-panel-header-test.mjs +83 -0
  402. package/scripts/topo-legend-pin-ring-fade-test.mjs +112 -0
  403. package/scripts/topo-legend-pin-test.mjs +128 -0
  404. package/scripts/topo-legend-row-hover-lift-test.mjs +109 -0
  405. package/scripts/topo-legend-row-labels-simplify-test.mjs +80 -0
  406. package/scripts/topo-legend-row-pin-spacing-test.mjs +124 -0
  407. package/scripts/topo-legend-row-tint-test.mjs +95 -0
  408. package/scripts/topo-legend-row-title-test.mjs +102 -0
  409. package/scripts/topo-legend-swatch-grow-test.mjs +116 -0
  410. package/scripts/topo-legend-swatch-radius-test.mjs +78 -0
  411. package/scripts/topo-minimap-a11y-test.mjs +101 -0
  412. package/scripts/topo-minimap-dot-transition-test.mjs +113 -0
  413. package/scripts/topo-minimap-radius-test.mjs +88 -0
  414. package/scripts/topo-minimap-rect-stroke-test.mjs +94 -0
  415. package/scripts/topo-minimap-viewport-smooth-test.mjs +124 -0
  416. package/scripts/topo-mobile-chips-test.mjs +103 -0
  417. package/scripts/topo-monogram-font-test.mjs +112 -0
  418. package/scripts/topo-monogram-stroke-test.mjs +101 -0
  419. package/scripts/topo-more-flows-letterspacing-test.mjs +111 -0
  420. package/scripts/topo-node-a11y-test.mjs +107 -0
  421. package/scripts/topo-node-alias-chat-pin-test.mjs +95 -0
  422. package/scripts/topo-node-hover-lift-test.mjs +85 -0
  423. package/scripts/topo-node-label-card-elevation-test.mjs +157 -0
  424. package/scripts/topo-node-label-card-stroke-test.mjs +135 -0
  425. package/scripts/topo-node-stroke-transition-test.mjs +103 -0
  426. package/scripts/topo-node-text-fill-transition-test.mjs +119 -0
  427. package/scripts/topo-node-title-peers-test.mjs +107 -0
  428. package/scripts/topo-node-title-test.mjs +101 -0
  429. package/scripts/topo-nodesize-active-hover-test.mjs +109 -0
  430. package/scripts/topo-nodesize-crossfade-test.mjs +141 -0
  431. package/scripts/topo-online-chip-nav-test.mjs +143 -0
  432. package/scripts/topo-orbit-particles-retired-test.mjs +91 -0
  433. package/scripts/topo-panel-count-unit-test.mjs +112 -0
  434. package/scripts/topo-panel-elevation-test.mjs +76 -0
  435. package/scripts/topo-panel-fadein-test.mjs +102 -0
  436. package/scripts/topo-panel-hot-tail-fade-test.mjs +132 -0
  437. package/scripts/topo-panel-hover-elevation-test.mjs +118 -0
  438. package/scripts/topo-panel-rx-test.mjs +90 -0
  439. package/scripts/topo-panel-theme-ease-test.mjs +85 -0
  440. package/scripts/topo-panel-title-fill-ease-test.mjs +91 -0
  441. package/scripts/topo-panel-title-tracking-test.mjs +78 -0
  442. package/scripts/topo-particle-stagger-test.mjs +82 -0
  443. package/scripts/topo-pin-intersection-color-ease-test.mjs +111 -0
  444. package/scripts/topo-pin-intersection-empty-test.mjs +129 -0
  445. package/scripts/topo-pin-intersection-tabular-test.mjs +96 -0
  446. package/scripts/topo-pin-intersection-test.mjs +121 -0
  447. package/scripts/topo-pin-intersection-warning-crossfade-test.mjs +107 -0
  448. package/scripts/topo-pin-mirror-transition-test.mjs +101 -0
  449. package/scripts/topo-pin-persistence-test.mjs +105 -0
  450. package/scripts/topo-popover-meta-test.mjs +78 -0
  451. package/scripts/topo-prefix-opacity-test.mjs +107 -0
  452. package/scripts/topo-prepublish-guard-test.mjs +95 -0
  453. package/scripts/topo-pressure-hover-test.mjs +123 -0
  454. package/scripts/topo-pressure-pin-test.mjs +119 -0
  455. package/scripts/topo-pressure-seg-self-hover-test.mjs +126 -0
  456. package/scripts/topo-pressure-seg-tooltip-test.mjs +98 -0
  457. package/scripts/topo-pressure-seg-transition-test.mjs +107 -0
  458. package/scripts/topo-pulse-dot-crossfade-test.mjs +122 -0
  459. package/scripts/topo-pulse-dot-retired-test.mjs +86 -0
  460. package/scripts/topo-recent-panel-count-freshness-test.mjs +109 -0
  461. package/scripts/topo-recent-panel-count-test.mjs +72 -0
  462. package/scripts/topo-recent-panel-count-weight-test.mjs +91 -0
  463. package/scripts/topo-recent-panel-footer-breath-test.mjs +142 -0
  464. package/scripts/topo-recent-panel-hot-header-test.mjs +132 -0
  465. package/scripts/topo-recent-panel-hot-tabular-test.mjs +113 -0
  466. package/scripts/topo-recent-panel-more-crossfade-test.mjs +124 -0
  467. package/scripts/topo-recent-panel-more-cyan-hover-test.mjs +151 -0
  468. package/scripts/topo-recent-panel-more-nav-test.mjs +127 -0
  469. package/scripts/topo-recent-panel-more-test.mjs +106 -0
  470. package/scripts/topo-recent-panel-readability-bump-test.mjs +151 -0
  471. package/scripts/topo-recent-row-count-fill-test.mjs +129 -0
  472. package/scripts/topo-recent-row-count-weight-test.mjs +112 -0
  473. package/scripts/topo-recent-row-fadein-test.mjs +107 -0
  474. package/scripts/topo-recent-row-freshness-test.mjs +102 -0
  475. package/scripts/topo-recent-row-hot-test.mjs +110 -0
  476. package/scripts/topo-recent-row-hover-lift-test.mjs +128 -0
  477. package/scripts/topo-recent-row-hover-test.mjs +122 -0
  478. package/scripts/topo-recent-row-pin-spacing-test.mjs +137 -0
  479. package/scripts/topo-recent-row-pin-test.mjs +126 -0
  480. package/scripts/topo-recent-row-tint-test.mjs +91 -0
  481. package/scripts/topo-recent-row-title-test.mjs +112 -0
  482. package/scripts/topo-recent-row-ts-alpha-test.mjs +100 -0
  483. package/scripts/topo-recent-row-ts-tabular-test.mjs +108 -0
  484. package/scripts/topo-recent-row-ts-test.mjs +95 -0
  485. package/scripts/topo-recent-row-typography-test.mjs +119 -0
  486. package/scripts/topo-recent-signal-empty-hint-test.mjs +73 -0
  487. package/scripts/topo-recent-signal-empty-test.mjs +63 -0
  488. package/scripts/topo-reduced-motion-test.mjs +77 -0
  489. package/scripts/topo-reset-spin-test.mjs +120 -0
  490. package/scripts/topo-runtime-badge-grow-test.mjs +114 -0
  491. package/scripts/topo-spokes-retired-test.mjs +93 -0
  492. package/scripts/topo-starfield-density-test.mjs +89 -0
  493. package/scripts/topo-status-chip-hover-test.mjs +101 -0
  494. package/scripts/topo-status-chip-tooltip-test.mjs +95 -0
  495. package/scripts/topo-statusbar-chips-self-hover-test.mjs +177 -0
  496. package/scripts/topo-svg-focus-test.mjs +112 -0
  497. package/scripts/topo-sweep-busyness-test.mjs +94 -0
  498. package/scripts/topo-tabular-nums-sweep-test.mjs +93 -0
  499. package/scripts/topo-tier-guide-dashes-test.mjs +86 -0
  500. package/scripts/topo-tier-ring-fadein-test.mjs +107 -0
  501. package/scripts/topo-tier-ring-occupancy-test.mjs +87 -0
  502. package/scripts/topo-tier-ring-test.mjs +69 -0
  503. package/scripts/topo-tier-ring-tint-test.mjs +110 -0
  504. package/scripts/topo-tier-stagger-test.mjs +89 -0
  505. package/scripts/topo-title-block-tight-leading-test.mjs +95 -0
  506. package/scripts/topo-title-tracking-test.mjs +98 -0
  507. package/scripts/topo-titleblock-gap-test.mjs +86 -0
  508. package/scripts/topo-titleblock-margin-test.mjs +83 -0
  509. package/scripts/topo-toggle-inactive-cyan-hover-test.mjs +110 -0
  510. package/scripts/topo-top-rail-theme-ease-test.mjs +89 -0
  511. package/scripts/topo-tz-ghost-test.mjs +69 -0
  512. package/scripts/topo-vendor-chip-hover-test.mjs +111 -0
  513. package/scripts/topo-vendor-chip-self-hover-test.mjs +131 -0
  514. package/scripts/topo-vendor-chip-tabular-test.mjs +92 -0
  515. package/scripts/topo-vendor-chip-threshold-test.mjs +78 -0
  516. package/scripts/topo-vendor-chip-weight-test.mjs +87 -0
  517. package/scripts/topo-vendor-count-suffix-test.mjs +95 -0
  518. package/scripts/topo-vendor-letter-tooltip-test.mjs +92 -0
  519. package/scripts/topo-vendor-pill-test.mjs +125 -0
  520. package/scripts/topo-vendor-pin-test.mjs +125 -0
  521. package/scripts/topo-view-smooth-test.mjs +131 -0
  522. package/scripts/topo-watermark-tracking-test.mjs +99 -0
  523. package/scripts/topo-working-chip-pin-test.mjs +154 -0
  524. package/scripts/topo-working-halo-breath-retired-test.mjs +99 -0
  525. package/scripts/topo-wrapper-flow-arrow-theme-test.mjs +97 -0
  526. package/scripts/topo-wrapper-radius-test.mjs +73 -0
  527. package/scripts/topo-wrapper-shadow-theme-ease-test.mjs +89 -0
  528. package/.next/static/chunks/0-7iowzu4.798.js +0 -1
  529. package/.next/static/chunks/02t5jr4shh.hy.js +0 -1
  530. package/.next/static/chunks/0_qfg5s5uzs7..js +0 -7
  531. package/.next/static/chunks/0elalb9n_.~di.js +0 -1
  532. package/.next/static/chunks/0j_q__o8p6g83.js +0 -1
  533. package/.next/static/chunks/0jl6_w50zh4l5.js +0 -1
  534. package/.next/static/chunks/0mqwn-l7wtcdo.js +0 -7
  535. package/.next/static/chunks/0r4q73.-1bkyl.js +0 -1
  536. package/.next/static/chunks/0r_c8eccg3z2o.js +0 -1
  537. package/.next/static/chunks/0tz5gaflhax.o.js +0 -1
  538. package/.next/static/chunks/10kk9kuoj0ns9.js +0 -1
  539. package/.next/static/chunks/10oi7l-tycgl9.js +0 -1
  540. package/.next/static/chunks/11l042tnn68ye.js +0 -1
  541. package/.next/static/chunks/11uk1izuv3~0t.js +0 -1
  542. package/.next/static/chunks/13.zti9ix.1eh.css +0 -2
  543. /package/.next/static/{zYWeA6KAnt4mH1Rredbmu → BR2xyE5iCb0X3_HnGmumr}/_buildManifest.js +0 -0
  544. /package/.next/static/{zYWeA6KAnt4mH1Rredbmu → BR2xyE5iCb0X3_HnGmumr}/_clientMiddlewareManifest.js +0 -0
  545. /package/.next/static/{zYWeA6KAnt4mH1Rredbmu → BR2xyE5iCb0X3_HnGmumr}/_ssgManifest.js +0 -0
@@ -0,0 +1,78 @@
1
+ /* Round 37 verification: ChatPopover header carries cwd / last-seen
2
+ * lines pulled from the SWR session cache. Three cases:
3
+ * - online node with cwd → cwd line, no last-seen
4
+ * - offline node with cwd → both lines
5
+ * - bare node (no cwd, no last_seen) → drag/Esc fallback hint */
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
+ async function probe(targetAlias) {
13
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1000 } });
14
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
15
+ await ctx.addInitScript(() => {
16
+ try {
17
+ localStorage.setItem('anet-theme', 'cyber');
18
+ localStorage.setItem('anet-topo-layout', 'grid');
19
+ localStorage.setItem('anet-topo-nodescale', '1');
20
+ sessionStorage.setItem('anet_v3_auth', '1');
21
+ } catch {}
22
+ });
23
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
24
+ const offline = new Date(Date.now() - 8 * 60 * 1000).toISOString(); // 8 min ago
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 sessions = [
30
+ { alias: 'liveCwd', status: 'idle', network_id: nid,
31
+ project_dir: '/home/vansin/agent-orchestra',
32
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh },
33
+ { alias: 'offCwd', status: 'offline', network_id: nid,
34
+ project_dir: '/home/vansin/some-project',
35
+ created_at: fresh, updated_at: fresh, last_seen_at: offline },
36
+ { alias: 'bare', status: 'idle', 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 } });
40
+ });
41
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
42
+ const page = await ctx.newPage();
43
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
44
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
45
+ await page.waitForTimeout(400);
46
+ await page.locator(`g[data-node="${targetAlias}"]`).click();
47
+ await page.waitForTimeout(300);
48
+ const meta = await page.evaluate(() => {
49
+ const cwd = document.querySelector('[data-popover-cwd]');
50
+ const ls = document.querySelector('[data-popover-lastseen]');
51
+ const popover = document.querySelector('[aria-label^="Chat with"]');
52
+ const dragHint = popover ? [...popover.querySelectorAll('div')].find(d => /Drag to move/.test(d.textContent || '')) : null;
53
+ return {
54
+ cwdText: cwd?.textContent || null,
55
+ lsText: ls?.textContent || null,
56
+ dragHintPresent: !!dragHint,
57
+ };
58
+ });
59
+ await ctx.close();
60
+ return meta;
61
+ }
62
+
63
+ const live = await probe('liveCwd');
64
+ const off = await probe('offCwd');
65
+ const bare = await probe('bare');
66
+ await browser.close();
67
+
68
+ const results = {
69
+ liveShowsCwd: live.cwdText === 'cwd: /home/vansin/agent-orchestra' && live.lsText === null && !live.dragHintPresent,
70
+ offlineShowsBoth: off.cwdText === 'cwd: /home/vansin/some-project'
71
+ && /last seen: [78]m ago/.test(off.lsText || '')
72
+ && !off.dragHintPresent,
73
+ bareFallsBackToDragHint: bare.cwdText === null && bare.lsText === null && bare.dragHintPresent,
74
+ };
75
+ const ok = Object.values(results).every(Boolean);
76
+ console.log(`${ok ? '✅' : '❌'} popover meta:`, JSON.stringify(results),
77
+ `\n live=`, live, `\n off=`, off, `\n bare=`, bare);
78
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,107 @@
1
+ /* Round 335 verification: filter pin prefix labels (`filter:` /
2
+ * `match:`) gain opacity-70 so the prefix recedes and the value
3
+ * text reads as primary content. Sibling to R333 vendor count
4
+ * suffix subordinate-lift family — recurring pattern: small
5
+ * "label" spans demote to opacity-70 while value text stays
6
+ * full-opacity, creating clear label-vs-value hierarchy inside
7
+ * the same chip.
8
+ *
9
+ * Five prefix surfaces share the `className="hidden sm:inline"`
10
+ * pattern (R335 replace_all):
11
+ * - status pin pill [data-filter-prefix]
12
+ * - group pin pill [data-filter-prefix]
13
+ * - vendor pin pill [data-filter-prefix]
14
+ * - edge pin pill [data-filter-prefix]
15
+ * - intersection chip [data-pin-intersection-prefix]
16
+ *
17
+ * Contract:
18
+ * - Activate a status pin (click working chip) so the status
19
+ * filter pin pill mounts with its `filter:` prefix visible.
20
+ * - The [data-filter-prefix] span has className containing
21
+ * 'opacity-70'.
22
+ * - Computed opacity reads 0.7.
23
+ * - R333 vendor count suffix still gray-400 + tabular.
24
+ * - R317/R318/R294 chrome + pulse regressions intact.
25
+ */
26
+ import { chromium } from 'playwright';
27
+ import { readFileSync } from 'node:fs';
28
+
29
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
30
+ const browser = await chromium.launch({ headless: true });
31
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
32
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
33
+ await ctx.addInitScript(() => {
34
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
35
+ });
36
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
37
+ await ctx.route('**/api/hub/status*', async (route) => {
38
+ const r = await route.fetch();
39
+ const b = await r.json();
40
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
41
+ const mk = (alias, model) => ({
42
+ alias, status: 'working', model, runtime: 'claude-code-cli',
43
+ network_id: nid, project_dir: null,
44
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
45
+ });
46
+ // 3 vendor types so the vendor letter chip row also renders (for
47
+ // R333 vendor count suffix regression check).
48
+ await route.fulfill({ response: r, json: { ...b, sessions: [
49
+ mk('alpha', 'claude-opus-4'),
50
+ mk('beta', 'gpt-4o'),
51
+ mk('gamma', 'internlm/internlm2'),
52
+ ] } });
53
+ });
54
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
55
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
56
+
57
+ const page = await ctx.newPage();
58
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
59
+ await page.waitForSelector('[data-working-chip]', { timeout: 15000 });
60
+ await page.waitForTimeout(200);
61
+
62
+ // Pin the working status to mount the status filter pill (which
63
+ // contains the `filter:` prefix).
64
+ await page.click('[data-working-chip]', { delay: 50 }).catch(() => {});
65
+ await page.waitForTimeout(400);
66
+
67
+ const probe = await page.evaluate(() => {
68
+ const prefixes = Array.from(document.querySelectorAll('[data-filter-prefix]'));
69
+ const intPrefix = document.querySelector('[data-pin-intersection-prefix]');
70
+ const vendorSuffix = document.querySelector('[data-vendor-letter-count-suffix]');
71
+ return {
72
+ prefixCount: prefixes.length,
73
+ prefixClasses: prefixes.map(p => p.className),
74
+ prefixOpacities: prefixes.map(p => getComputedStyle(p).opacity),
75
+ intPrefixClass: intPrefix?.className ?? null,
76
+ intPrefixOpacity: intPrefix ? getComputedStyle(intPrefix).opacity : null,
77
+ vendorSuffixClass: vendorSuffix?.className ?? '',
78
+ vendorSuffixTab: vendorSuffix ? getComputedStyle(vendorSuffix).fontVariantNumeric : null,
79
+ layoutInactiveCls: document.querySelector('[data-topo-chrome-layout="grid"]')?.className ?? '',
80
+ layoutActiveCls: document.querySelector('[data-topo-chrome-layout="ring"]')?.className ?? '',
81
+ pulseCount: document.querySelectorAll('[data-pulse-wrapper]').length,
82
+ };
83
+ });
84
+ await browser.close();
85
+
86
+ const hasOpacity70 = (cls) => /opacity-70/.test(cls || '');
87
+ const opacityClose = (val) => Math.abs(parseFloat(val) - 0.7) < 0.01;
88
+
89
+ const results = {
90
+ prefix_at_least_one: probe.prefixCount >= 1,
91
+ all_prefix_have_opacity70: probe.prefixClasses.every(hasOpacity70),
92
+ all_prefix_computed_0_7: probe.prefixOpacities.every(opacityClose),
93
+ // Pin-intersection chip only mounts when ≥2 dims pinned, which we
94
+ // didn't trigger here (only one click). Accept null OR opacity-70.
95
+ intersection_prefix_ok: probe.intPrefixClass === null || hasOpacity70(probe.intPrefixClass),
96
+ // R333 vendor count suffix regression.
97
+ r333_vendor_suffix_gray400: probe.vendorSuffixClass.includes('text-gray-400'),
98
+ r333_vendor_suffix_tabular: /tabular-nums/.test(probe.vendorSuffixTab || ''),
99
+ // R317 / R318 chrome regression.
100
+ r317_inactive_gray_400: probe.layoutInactiveCls.includes('text-gray-400'),
101
+ r318_active_font_medium: probe.layoutActiveCls.includes('font-medium'),
102
+ r294_pulse_absent: probe.pulseCount === 0,
103
+ };
104
+ const ok = Object.values(results).every(Boolean);
105
+ console.log(`${ok ? '✅' : '❌'} prefix opacity-70:`, JSON.stringify(results),
106
+ '\n prefix opacities:', probe.prefixOpacities);
107
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,95 @@
1
+ /* Round 238 verification: prepublishOnly is now a guard-only check
2
+ * instead of a destructive rebuild. This closes the chunk-500 root
3
+ * cause documented since R224.
4
+ *
5
+ * Pre-R238 prepublishOnly was 'rm -rf .next && npm run build', which:
6
+ * 1. Deleted .next/ on disk during npm publish.
7
+ * 2. Rebuilt with fresh content-hashed chunk filenames.
8
+ * 3. Left the already-running next-server process with a stale
9
+ * in-memory manifest pointing at chunks that no longer existed
10
+ * on disk → ENOENT → 500 on lazy chunk fetch.
11
+ * 4. Required a manual 'restart dash AFTER publish' step every
12
+ * round as a workaround.
13
+ *
14
+ * Post-R238 prepublishOnly is a guard:
15
+ * '[ -f .next/BUILD_ID ] || (echo ... && exit 1)'
16
+ *
17
+ * - If .next/BUILD_ID exists, succeed silently — publish proceeds
18
+ * with whatever .next/ is on disk (intentional: pipeline order
19
+ * is now bump → build → restart → test → publish, so the build
20
+ * already embeds the new version BEFORE publish).
21
+ * - If .next/BUILD_ID is missing, fail loudly with the R224
22
+ * reference so the developer knows what to do.
23
+ *
24
+ * No rm-rf-rebuild = running server's manifest stays consistent
25
+ * with disk chunks through the publish step. No post-publish
26
+ * restart needed.
27
+ *
28
+ * Test verifies:
29
+ * 1. package.json scripts.prepublishOnly contains no 'rm' command
30
+ * (destructive form removed)
31
+ * 2. package.json scripts.prepublishOnly contains no 'npm run
32
+ * build' (rebuild on publish removed)
33
+ * 3. The guard form is present (BUILD_ID check)
34
+ * 4. Running `npm run prepublishOnly` exits 0 when .next/BUILD_ID
35
+ * exists, leaving .next/ untouched (mtime + content hash stable)
36
+ */
37
+ import { readFileSync, statSync } from 'node:fs';
38
+ import { execSync } from 'node:child_process';
39
+ import { createHash } from 'node:crypto';
40
+
41
+ const pkg = JSON.parse(readFileSync('/home/vansin/agent-network-dashboard/package.json', 'utf8'));
42
+ const hookSrc = pkg.scripts?.prepublishOnly || '';
43
+
44
+ // Hash + mtime of .next/BUILD_ID before running the hook
45
+ const buildIdPath = '/home/vansin/agent-network-dashboard/.next/BUILD_ID';
46
+ const before = (() => {
47
+ try {
48
+ return {
49
+ mtime: statSync(buildIdPath).mtimeMs,
50
+ hash: createHash('sha256').update(readFileSync(buildIdPath)).digest('hex'),
51
+ };
52
+ } catch {
53
+ return null;
54
+ }
55
+ })();
56
+
57
+ // Run the prepublishOnly hook explicitly via npm run
58
+ let exitCode = -1;
59
+ try {
60
+ execSync('npm run prepublishOnly', {
61
+ cwd: '/home/vansin/agent-network-dashboard',
62
+ stdio: 'pipe',
63
+ });
64
+ exitCode = 0;
65
+ } catch (e) {
66
+ exitCode = e.status ?? -1;
67
+ }
68
+
69
+ const after = (() => {
70
+ try {
71
+ return {
72
+ mtime: statSync(buildIdPath).mtimeMs,
73
+ hash: createHash('sha256').update(readFileSync(buildIdPath)).digest('hex'),
74
+ };
75
+ } catch {
76
+ return null;
77
+ }
78
+ })();
79
+
80
+ const results = {
81
+ hook_present: typeof hookSrc === 'string' && hookSrc.length > 0,
82
+ no_rm_command: !/\brm\b/.test(hookSrc),
83
+ has_build_id_check: /\.next\/BUILD_ID/.test(hookSrc),
84
+ has_exit_path: /exit\s+1/.test(hookSrc),
85
+ hook_exits_zero: exitCode === 0,
86
+ build_id_mtime_stable: before !== null && after !== null && before.mtime === after.mtime,
87
+ build_id_hash_stable: before !== null && after !== null && before.hash === after.hash,
88
+ };
89
+ const ok = Object.values(results).every(Boolean);
90
+ console.log(`${ok ? '✅' : '❌'} prepublishOnly guard:`, JSON.stringify(results),
91
+ '\n hookSrc:', hookSrc,
92
+ '\n exitCode:', exitCode,
93
+ '\n before:', before,
94
+ '\n after: ', after);
95
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,123 @@
1
+ /* Round 83 verification: pressure-bar segments expose a hover preview
2
+ * before the click pins. Matches the R55 legend + R79 status chip +
3
+ * R80 vendor letter hover pattern — same activeStatus mechanism, just
4
+ * the last surface that was click-only.
5
+ *
6
+ * Fleet: 1 working + 2 idle + 1 offline. Hover the working segment →
7
+ * working node stays bright, idle + offline dim. Mouseleave → restore.
8
+ * Then click the working segment → pin sticks via boxShadow. Hovering
9
+ * idle while working is pinned → live preview switches; mouseleave
10
+ * falls back to the working pin (activeStatus = hoveredStatus ??
11
+ * pinnedStatus).
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: 1200 } });
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
+ sessionStorage.setItem('anet_v3_auth', '1');
25
+ sessionStorage.removeItem('anet-topo-pinned-status');
26
+ sessionStorage.removeItem('anet-topo-pinned-group');
27
+ } catch {}
28
+ });
29
+
30
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
31
+ const stale10 = new Date(Date.now() - 10 * 60 * 1000).toISOString();
32
+ await ctx.route('**/api/hub/status*', async (route) => {
33
+ const r = await route.fetch();
34
+ const b = await r.json();
35
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
36
+ const sessions = [
37
+ { alias: 'wkr', status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
38
+ network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
39
+ { alias: 'idl1', status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
40
+ network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
41
+ { alias: 'idl2', status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
42
+ network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
43
+ { alias: 'off', status: 'offline', model: 'claude-opus-4', runtime: 'cli-claude-code',
44
+ network_id: nid, project_dir: null, created_at: stale10, updated_at: stale10, last_seen_at: stale10 },
45
+ ];
46
+ await route.fulfill({ response: r, json: { ...b, sessions } });
47
+ });
48
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
49
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
50
+ const page = await ctx.newPage();
51
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
52
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
53
+ await page.waitForTimeout(500);
54
+
55
+ const ops = () => page.evaluate(() => {
56
+ const o = {};
57
+ for (const a of ['wkr', 'idl1', 'idl2', 'off']) {
58
+ const g = document.querySelector(`g[data-node="${a}"]`);
59
+ o[a] = g ? +(g.style.opacity || '1') : null;
60
+ }
61
+ return o;
62
+ });
63
+ const segShadow = (k) => page.evaluate((key) => {
64
+ const s = document.querySelector(`[data-pressure-seg="${key}"]`);
65
+ return s ? (s.getAttribute('style') || '').includes('inset') : false;
66
+ }, k);
67
+
68
+ const before = await ops();
69
+
70
+ // Hover working segment → working stays bright, others dim.
71
+ await page.locator('[data-pressure-seg="working"]').hover();
72
+ await page.waitForTimeout(250);
73
+ const onHoverWorking = await ops();
74
+ const hoverPinShadowBefore = await segShadow('working');
75
+
76
+ // Mouseleave → baseline restores (no pin yet).
77
+ await page.mouse.move(10, 10);
78
+ await page.waitForTimeout(250);
79
+ const afterLeave = await ops();
80
+
81
+ // Click to pin working — segment gets inset shadow.
82
+ await page.locator('[data-pressure-seg="working"]').click();
83
+ await page.mouse.move(10, 10);
84
+ await page.waitForTimeout(250);
85
+ const afterPin = await ops();
86
+ const pinShadow = await segShadow('working');
87
+
88
+ // Hover idle while working is pinned — hoveredStatus overrides for the
89
+ // duration of the hover. idle stays bright, working dims (transient).
90
+ await page.locator('[data-pressure-seg="idle"]').hover();
91
+ await page.waitForTimeout(250);
92
+ const onHoverIdle = await ops();
93
+
94
+ // Mouseleave → falls back to working pin (NOT cleared).
95
+ await page.mouse.move(10, 10);
96
+ await page.waitForTimeout(250);
97
+ const afterLeaveIdle = await ops();
98
+
99
+ await browser.close();
100
+
101
+ const bright = (v) => v != null && v >= 0.55;
102
+ const dim = (v) => v != null && v < 0.4;
103
+ const results = {
104
+ before_baseline: bright(before.wkr) && bright(before.idl1) && bright(before.idl2) && before.off <= 0.6,
105
+ hoverWorking_workerStays: bright(onHoverWorking.wkr),
106
+ hoverWorking_idlesDim: dim(onHoverWorking.idl1) && dim(onHoverWorking.idl2),
107
+ hoverWorking_offDim: dim(onHoverWorking.off),
108
+ hoverWorking_noPinYet: !hoverPinShadowBefore,
109
+ leave_restoresBaseline: bright(afterLeave.wkr) && bright(afterLeave.idl1) && bright(afterLeave.idl2),
110
+ pin_workerStays: bright(afterPin.wkr),
111
+ pin_idlesDim: dim(afterPin.idl1) && dim(afterPin.idl2),
112
+ pin_shadowRendered: pinShadow,
113
+ hoverIdle_transient: bright(onHoverIdle.idl1) && bright(onHoverIdle.idl2) && dim(onHoverIdle.wkr),
114
+ leaveIdle_pinPersists: bright(afterLeaveIdle.wkr) && dim(afterLeaveIdle.idl1) && dim(afterLeaveIdle.idl2),
115
+ };
116
+ const ok = Object.values(results).every(Boolean);
117
+ console.log(`${ok ? '✅' : '❌'} pressure-hover:`, JSON.stringify(results),
118
+ `\n before=`, before,
119
+ `\n onHoverWorking=`, onHoverWorking,
120
+ `\n afterPin=`, afterPin,
121
+ `\n onHoverIdle=`, onHoverIdle,
122
+ `\n afterLeaveIdle=`, afterLeaveIdle);
123
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,119 @@
1
+ /* Round 60 verification: each pressure-bar segment toggles a sticky
2
+ * status filter. Click "working" → all non-working nodes dim, working
3
+ * ones stay. Click again → release. Pin and the R55 legend hover feed
4
+ * the same node-opacity branch via `activeStatus = hoveredStatus ??
5
+ * pinnedStatus`, so hover transiently overrides without nuking the pin.
6
+ *
7
+ * Sessions:
8
+ * wkr — working
9
+ * idl1 — idle
10
+ * idl2 — idle
11
+ * off — offline
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
+ const stale = new Date(Date.now() - 30 * 60 * 1000).toISOString();
31
+ await ctx.route('**/api/hub/status*', async (route) => {
32
+ const r = await route.fetch();
33
+ const b = await r.json();
34
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
35
+ const sessions = [
36
+ { alias: 'wkr', status: 'working', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
37
+ { alias: 'idl1', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
38
+ { alias: 'idl2', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
39
+ { alias: 'off', status: 'offline', network_id: nid, project_dir: null, created_at: stale, updated_at: stale, last_seen_at: stale },
40
+ ];
41
+ await route.fulfill({ response: r, json: { ...b, sessions } });
42
+ });
43
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
44
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
45
+ const page = await ctx.newPage();
46
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
47
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
48
+ await page.waitForTimeout(500);
49
+
50
+ const opacities = () => page.evaluate(() => {
51
+ const o = {};
52
+ for (const a of ['wkr', 'idl1', 'idl2', 'off']) {
53
+ const g = document.querySelector(`g[data-node="${a}"]`);
54
+ o[a] = g ? +(g.style.opacity || '1') : null;
55
+ }
56
+ return o;
57
+ });
58
+
59
+ const before = await opacities();
60
+
61
+ const bright = (v) => v != null && v >= 0.55;
62
+ const dim = (v) => v != null && v < 0.4;
63
+
64
+ // Click the WORKING segment.
65
+ const workingSeg = page.locator('[data-pressure-seg="working"]').first();
66
+ const workingExists = await workingSeg.count() > 0;
67
+ if (!workingExists) { console.log('❌ pressure segment[working] not found'); process.exit(1); }
68
+ await workingSeg.click({ force: true });
69
+ await page.waitForTimeout(250);
70
+ const afterPin = await opacities();
71
+
72
+ // Pin should persist past mouse moving away.
73
+ await page.mouse.move(10, 10);
74
+ await page.waitForTimeout(250);
75
+ const afterMouseMove = await opacities();
76
+
77
+ // Click again to release. R79 added hover-affordances on the working
78
+ // + online chips, so the click locator's mouse path can light a
79
+ // transient hoveredStatus en route to the pressure segment. Mouse-move
80
+ // to a neutral spot after the release click so we observe pin-only
81
+ // state (same gotcha R61's test documented inline).
82
+ await workingSeg.click({ force: true });
83
+ await page.mouse.move(10, 10);
84
+ await page.waitForTimeout(250);
85
+ const afterRelease = await opacities();
86
+
87
+ // Repeat for offline segment.
88
+ const offlineSeg = page.locator('[data-pressure-seg="offline"]').first();
89
+ await offlineSeg.click({ force: true });
90
+ await page.mouse.move(10, 10);
91
+ await page.waitForTimeout(250);
92
+ const afterOfflinePin = await opacities();
93
+ await offlineSeg.click({ force: true });
94
+ await page.mouse.move(10, 10);
95
+ await page.waitForTimeout(250);
96
+ const afterOfflineRelease = await opacities();
97
+
98
+ await browser.close();
99
+
100
+ const results = {
101
+ before_baseline: bright(before.wkr) && bright(before.idl1),
102
+ working_pinKeepsWorking: bright(afterPin.wkr),
103
+ working_pinDimsIdle: dim(afterPin.idl1) && dim(afterPin.idl2),
104
+ working_pinDimsOffline: dim(afterPin.off),
105
+ working_pinSurvivesMouse: bright(afterMouseMove.wkr) && dim(afterMouseMove.idl1),
106
+ working_releaseRestoresAll: bright(afterRelease.wkr) && bright(afterRelease.idl1),
107
+ offline_pinKeepsOffline: bright(afterOfflinePin.off), // base offline 0.6 reads as bright (>=0.55)
108
+ offline_pinDimsOnline: dim(afterOfflinePin.wkr) && dim(afterOfflinePin.idl1),
109
+ offline_releaseRestoresAll: bright(afterOfflineRelease.wkr),
110
+ };
111
+ const ok = Object.values(results).every(Boolean);
112
+ console.log(`${ok ? '✅' : '❌'} pressure pin:`, JSON.stringify(results),
113
+ `\n before=`, before,
114
+ `\n afterPin=`, afterPin,
115
+ `\n afterMouseMove=`, afterMouseMove,
116
+ `\n afterRelease=`, afterRelease,
117
+ `\n afterOfflinePin=`, afterOfflinePin,
118
+ `\n afterOfflineRelease=`, afterOfflineRelease);
119
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,126 @@
1
+ /* Round 210 verification: pressure-bar segments deepen their OWN
2
+ * colour on hover via filter: brightness(1.2). Closes the chip-row
3
+ * "hover deepens own identity" family at a 6th surface (the
4
+ * pressure-bar scope previously had R83 wire setHoveredStatus but
5
+ * the segment itself stayed flat).
6
+ *
7
+ * Pre-R210 hovering a segment fired R55 canvas dim via
8
+ * setHoveredStatus, but the segment's own background stayed at the
9
+ * unmodified tier color. R210 makes the cause element (segment)
10
+ * light up the same gesture it fires on the canvas (effect element).
11
+ *
12
+ * Test (cyber dark, 2 working + 1 idle + 1 offline → 3 segments):
13
+ * 1. 3 [data-pressure-seg] segments rendered.
14
+ * 2. Idle: every segment has hovered='false', filter='none' (or
15
+ * no filter style set).
16
+ * 3. Inline transition includes 'filter 150ms' alongside the
17
+ * existing R165 width 220ms + R60 box-shadow 150ms.
18
+ * 4. Hover working segment → working hovered='true', filter
19
+ * 'brightness(1.2)'.
20
+ * 5. Other (idle/offline) segments stay hovered='false', no filter.
21
+ * 6. Move away → working back to hovered='false', no filter.
22
+ */
23
+ import { chromium } from 'playwright';
24
+ import { readFileSync } from 'node:fs';
25
+
26
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
27
+ const browser = await chromium.launch({ headless: true });
28
+
29
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
30
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
31
+ await ctx.addInitScript(() => {
32
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
33
+ });
34
+
35
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
36
+ await ctx.route('**/api/hub/status*', async (route) => {
37
+ const r = await route.fetch();
38
+ const b = await r.json();
39
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
40
+ const mk = (alias, status) => ({
41
+ alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
42
+ network_id: nid, project_dir: null,
43
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
44
+ });
45
+ await route.fulfill({ response: r, json: { ...b, sessions: [
46
+ mk('alpha', 'working'),
47
+ mk('beta', 'working'),
48
+ mk('gamma', 'idle'),
49
+ mk('delta', 'offline'),
50
+ ] } });
51
+ });
52
+ await ctx.route('**/api/hub/messages*', (r2) => r2.fulfill({ json: { messages: [] } }));
53
+ await ctx.route('**/api/hub/tasks*', (r2) => r2.fulfill({ json: { tasks: [] } }));
54
+
55
+ const page = await ctx.newPage();
56
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
57
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
58
+ await page.waitForSelector('[data-pressure-seg="working"]', { timeout: 10000 });
59
+ await page.waitForTimeout(300);
60
+
61
+ const probe = async () => page.evaluate(() => {
62
+ return [...document.querySelectorAll('[data-pressure-seg]')].map(el => {
63
+ const cs = getComputedStyle(el);
64
+ return {
65
+ key: el.getAttribute('data-pressure-seg'),
66
+ hovered: el.getAttribute('data-pressure-seg-hovered'),
67
+ filter: cs.filter,
68
+ inlineFilter: el.style.filter,
69
+ transition: el.style.transition,
70
+ };
71
+ });
72
+ });
73
+
74
+ const idle = await probe();
75
+
76
+ await page.hover('[data-pressure-seg="working"]');
77
+ await page.waitForTimeout(180);
78
+ const hovered = await probe();
79
+
80
+ await page.mouse.move(0, 0);
81
+ await page.waitForTimeout(180);
82
+ const settled = await probe();
83
+
84
+ await browser.close();
85
+
86
+ const idleWorking = idle.find(s => s.key === 'working');
87
+ const idleIdle = idle.find(s => s.key === 'idle');
88
+ const idleOffline = idle.find(s => s.key === 'offline');
89
+ const hovWorking = hovered.find(s => s.key === 'working');
90
+ const hovIdle = hovered.find(s => s.key === 'idle');
91
+ const settledWorking = settled.find(s => s.key === 'working');
92
+
93
+ const isNoFilter = (s) => !s || s === 'none' || s === '';
94
+ const hasBrightness = (s) => /brightness\s*\(\s*1\.2/.test(s || '');
95
+
96
+ const results = {
97
+ three_segments_present: idle.length === 3,
98
+ working_found: !!idleWorking,
99
+ idle_found: !!idleIdle,
100
+ offline_found: !!idleOffline,
101
+
102
+ // Idle: no filter, hovered='false', transitions present
103
+ idle_all_not_hovered: idle.every(s => s.hovered === 'false'),
104
+ idle_all_no_filter: idle.every(s => isNoFilter(s.inlineFilter)),
105
+ transitions_have_filter: idle.every(s => /filter\s+(?:150ms|0\.15s)/.test(s.transition || '')),
106
+ transitions_keep_width: idle.every(s => /width\s+(?:220ms|0\.22s)/.test(s.transition || '')),
107
+ transitions_keep_bs: idle.every(s => /box-shadow\s+(?:150ms|0\.15s)/.test(s.transition || '')),
108
+
109
+ // Hover working: brightness applied
110
+ hov_working_hovered_true: hovWorking?.hovered === 'true',
111
+ hov_working_has_filter: hasBrightness(hovWorking?.inlineFilter)
112
+ || hasBrightness(hovWorking?.filter),
113
+ // Other segments stay idle
114
+ hov_idle_stays_false: hovIdle?.hovered === 'false',
115
+ hov_idle_no_filter: isNoFilter(hovIdle?.inlineFilter),
116
+
117
+ // Settled: back to idle
118
+ settled_hovered_false: settledWorking?.hovered === 'false',
119
+ settled_no_filter: isNoFilter(settledWorking?.inlineFilter),
120
+ };
121
+ const ok = Object.values(results).every(Boolean);
122
+ console.log(`${ok ? '✅' : '❌'} pressure-seg self-hover:`, JSON.stringify(results),
123
+ `\n idle:`, idle,
124
+ `\n hovered:`, hovered,
125
+ `\n settled:`, settled);
126
+ process.exit(ok ? 0 : 1);