@sleep2agi/agent-network-dashboard 0.5.3 → 0.5.4

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 (484) 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 +35 -35
  5. package/.next/fallback-build-manifest.json +3 -3
  6. package/.next/prerender-manifest.json +29 -0
  7. package/.next/routes-manifest.json +12 -0
  8. package/.next/server/app/_global-error.html +1 -1
  9. package/.next/server/app/_global-error.rsc +2 -2
  10. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_full.segment.rsc +2 -2
  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 +13 -13
  18. package/.next/server/app/_not-found.segments/_full.segment.rsc +13 -13
  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 +15 -15
  27. package/.next/server/app/admin.segments/_full.segment.rsc +15 -15
  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/upload/route/app-paths-manifest.json +3 -0
  34. package/.next/server/app/api/hub/upload/route/build-manifest.json +9 -0
  35. package/.next/server/app/api/hub/upload/route/server-reference-manifest.json +4 -0
  36. package/.next/server/app/api/hub/upload/route.js +7 -0
  37. package/.next/server/app/api/hub/upload/route.js.map +5 -0
  38. package/.next/server/app/api/hub/upload/route.js.nft.json +1 -0
  39. package/.next/server/app/api/hub/upload/route_client-reference-manifest.js +3 -0
  40. package/.next/server/app/index.html +2 -2
  41. package/.next/server/app/index.rsc +15 -15
  42. package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
  43. package/.next/server/app/index.segments/_full.segment.rsc +15 -15
  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 +15 -15
  50. package/.next/server/app/login.segments/_full.segment.rsc +15 -15
  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 +15 -15
  59. package/.next/server/app/logs.segments/_full.segment.rsc +15 -15
  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/manifest.webmanifest/route/app-paths-manifest.json +3 -0
  66. package/.next/server/app/manifest.webmanifest/route/build-manifest.json +9 -0
  67. package/.next/server/app/manifest.webmanifest/route/server-reference-manifest.json +4 -0
  68. package/.next/server/app/manifest.webmanifest/route.js +8 -0
  69. package/.next/server/app/manifest.webmanifest/route.js.map +5 -0
  70. package/.next/server/app/manifest.webmanifest/route.js.nft.json +1 -0
  71. package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +3 -0
  72. package/.next/server/app/manifest.webmanifest.body +1 -0
  73. package/.next/server/app/manifest.webmanifest.meta +1 -0
  74. package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
  75. package/.next/server/app/messages.html +2 -2
  76. package/.next/server/app/messages.rsc +15 -15
  77. package/.next/server/app/messages.segments/_full.segment.rsc +15 -15
  78. package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
  79. package/.next/server/app/messages.segments/_index.segment.rsc +7 -7
  80. package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
  81. package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
  82. package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
  83. package/.next/server/app/node/page_client-reference-manifest.js +1 -1
  84. package/.next/server/app/node.html +2 -2
  85. package/.next/server/app/node.rsc +15 -15
  86. package/.next/server/app/node.segments/_full.segment.rsc +15 -15
  87. package/.next/server/app/node.segments/_head.segment.rsc +4 -4
  88. package/.next/server/app/node.segments/_index.segment.rsc +7 -7
  89. package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
  90. package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
  91. package/.next/server/app/node.segments/node.segment.rsc +3 -3
  92. package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
  93. package/.next/server/app/nodes.html +2 -2
  94. package/.next/server/app/nodes.rsc +15 -15
  95. package/.next/server/app/nodes.segments/_full.segment.rsc +15 -15
  96. package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
  97. package/.next/server/app/nodes.segments/_index.segment.rsc +7 -7
  98. package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
  99. package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
  100. package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
  101. package/.next/server/app/page_client-reference-manifest.js +1 -1
  102. package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
  103. package/.next/server/app/server-logs.html +2 -2
  104. package/.next/server/app/server-logs.rsc +15 -15
  105. package/.next/server/app/server-logs.segments/_full.segment.rsc +15 -15
  106. package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
  107. package/.next/server/app/server-logs.segments/_index.segment.rsc +7 -7
  108. package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
  109. package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
  110. package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
  111. package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
  112. package/.next/server/app/settings/networks.html +2 -2
  113. package/.next/server/app/settings/networks.rsc +15 -15
  114. package/.next/server/app/settings/networks.segments/_full.segment.rsc +15 -15
  115. package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
  116. package/.next/server/app/settings/networks.segments/_index.segment.rsc +7 -7
  117. package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
  118. package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
  119. package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
  120. package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
  121. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  122. package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
  123. package/.next/server/app/settings/tokens.html +2 -2
  124. package/.next/server/app/settings/tokens.rsc +15 -15
  125. package/.next/server/app/settings/tokens.segments/_full.segment.rsc +15 -15
  126. package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
  127. package/.next/server/app/settings/tokens.segments/_index.segment.rsc +7 -7
  128. package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
  129. package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
  130. package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
  131. package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
  132. package/.next/server/app/settings.html +2 -2
  133. package/.next/server/app/settings.rsc +15 -15
  134. package/.next/server/app/settings.segments/_full.segment.rsc +15 -15
  135. package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
  136. package/.next/server/app/settings.segments/_index.segment.rsc +7 -7
  137. package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  138. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
  139. package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
  140. package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
  141. package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
  142. package/.next/server/app/tasks.html +2 -2
  143. package/.next/server/app/tasks.rsc +15 -15
  144. package/.next/server/app/tasks.segments/_full.segment.rsc +15 -15
  145. package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
  146. package/.next/server/app/tasks.segments/_index.segment.rsc +7 -7
  147. package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
  148. package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
  149. package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
  150. package/.next/server/app-paths-manifest.json +2 -0
  151. package/.next/server/chunks/00jm_next_dist_0ju_ux9._.js +18 -0
  152. package/.next/server/chunks/00jm_next_dist_0ju_ux9._.js.map +1 -0
  153. package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_upload_route_actions_03_eqnf.js +3 -0
  154. package/.next/server/chunks/0ykm__next-internal_server_app_manifest_webmanifest_route_actions_0m16rb8.js +3 -0
  155. package/.next/server/chunks/0ykm__next-internal_server_app_manifest_webmanifest_route_actions_0m16rb8.js.map +1 -0
  156. package/.next/server/chunks/{[externals]__11vad82._.js → [externals]__036g1.i._.js} +2 -2
  157. package/.next/server/chunks/[externals]__036g1.i._.js.map +1 -0
  158. package/.next/server/chunks/[root-of-the-server]__0-8_f0o._.js +3 -0
  159. package/.next/server/chunks/[root-of-the-server]__0-8_f0o._.js.map +1 -0
  160. package/.next/server/chunks/{[root-of-the-server]__08uyvdq._.js → [root-of-the-server]__0jndzts._.js} +2 -2
  161. package/.next/server/chunks/{[root-of-the-server]__08uyvdq._.js.map → [root-of-the-server]__0jndzts._.js.map} +1 -1
  162. package/.next/server/chunks/[root-of-the-server]__0mxw4vb._.js +1 -1
  163. package/.next/server/chunks/[root-of-the-server]__0mxw4vb._.js.map +1 -1
  164. package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js +1 -1
  165. package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js.map +1 -1
  166. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0..ddwm.js +1 -1
  167. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0..ddwm.js.map +1 -1
  168. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_02bxi6j.js +1 -1
  169. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_02bxi6j.js.map +1 -1
  170. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_05oassd.js +1 -1
  171. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_05oassd.js.map +1 -1
  172. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_05vxtby.js +1 -1
  173. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_05vxtby.js.map +1 -1
  174. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_05~eml4.js +1 -1
  175. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_05~eml4.js.map +1 -1
  176. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0_-g_q2.js +1 -1
  177. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0_-g_q2.js.map +1 -1
  178. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0_ec0xu.js +1 -1
  179. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0_ec0xu.js.map +1 -1
  180. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0czb6wz.js +1 -1
  181. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0czb6wz.js.map +1 -1
  182. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0e-cn.p.js +1 -1
  183. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0e-cn.p.js.map +1 -1
  184. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0k8_sr8.js +1 -1
  185. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0k8_sr8.js.map +1 -1
  186. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0lt_3bw.js +1 -1
  187. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0lt_3bw.js.map +1 -1
  188. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0uyh3yt.js +1 -1
  189. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0uyh3yt.js.map +1 -1
  190. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0v-a2ps.js +1 -1
  191. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_0v-a2ps.js.map +1 -1
  192. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_12.4r9~.js +1 -1
  193. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_12.4r9~.js.map +1 -1
  194. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_13mqkay.js +1 -1
  195. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_13mqkay.js.map +1 -1
  196. package/.next/server/chunks/ssr/[root-of-the-server]__030vg4n._.js +4 -1
  197. package/.next/server/chunks/ssr/[root-of-the-server]__030vg4n._.js.map +1 -1
  198. package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js +1 -1
  199. package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js.map +1 -1
  200. package/.next/server/chunks/ssr/[root-of-the-server]__0u5aqkk._.js +1 -1
  201. package/.next/server/chunks/ssr/[root-of-the-server]__0u5aqkk._.js.map +1 -1
  202. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +4 -4
  203. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
  204. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
  205. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
  206. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
  207. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
  208. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js +1 -1
  209. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js.map +1 -1
  210. package/.next/server/middleware-build-manifest.js +3 -3
  211. package/.next/server/middleware.js +2 -2
  212. package/.next/server/middleware.js.nft.json +1 -1
  213. package/.next/server/pages/404.html +2 -2
  214. package/.next/server/pages/500.html +1 -1
  215. package/.next/static/chunks/{07~8sx3_5uiox.js → 04wjx7vbxusw5.js} +4 -1
  216. package/.next/static/chunks/089t1exs6apb8.js +4 -0
  217. package/.next/static/chunks/{0ph1in3421~o-.js → 09e8kxo30n5cf.js} +1 -1
  218. package/.next/static/chunks/{0v4-5tng.uh.7.js → 0k-c1chkvf78s.js} +2 -2
  219. package/.next/static/chunks/0v2~nlpk-cx6v.css +2 -0
  220. package/.next/static/chunks/0xsye-9kffdi0.js +1 -0
  221. package/.next/static/chunks/0~rx_~akeylmq.js +1 -0
  222. package/.next/trace +2 -2
  223. package/.next/trace-build +1 -1
  224. package/.next/types/routes.d.ts +2 -1
  225. package/.next/types/validator.ts +9 -0
  226. package/app/api/hub/send/route.ts +11 -2
  227. package/app/api/hub/servers/route.ts +3 -29
  228. package/app/api/hub/upload/route.ts +83 -0
  229. package/app/components/AppShell.tsx +3 -1
  230. package/app/components/MobileNav.tsx +45 -0
  231. package/app/components/TaskChatPanel.tsx +130 -6
  232. package/app/components/TopoGraph.tsx +6686 -248
  233. package/app/globals.css +897 -7
  234. package/app/layout.tsx +6 -0
  235. package/app/lib/serverDedupe.ts +122 -0
  236. package/app/lib/vendorIdentity.ts +74 -56
  237. package/app/manifest.ts +23 -0
  238. package/package.json +4 -4
  239. package/public/vendors/claude.svg +7 -8
  240. package/public/vendors/minimax.svg +8 -9
  241. package/public/vendors/openai.svg +8 -10
  242. package/scripts/p157-rc2-dedup-test.mjs +110 -0
  243. package/scripts/topo-a11y-titles-catalog-test.mjs +132 -0
  244. package/scripts/topo-active-chrome-hover-text-test.mjs +107 -0
  245. package/scripts/topo-active-links-chip-halo-layers-test.mjs +81 -0
  246. package/scripts/topo-alias-chat-brightness-test.mjs +79 -0
  247. package/scripts/topo-alias-text-halo-layers-test.mjs +98 -0
  248. package/scripts/topo-animation-temporal-modes-catalog-test.mjs +144 -0
  249. package/scripts/topo-avatar-chat-gate-test.mjs +77 -0
  250. package/scripts/topo-avatar-drop-shadow-test.mjs +86 -0
  251. package/scripts/topo-avatar-fallback-hover-test.mjs +104 -0
  252. package/scripts/topo-avatar-fallback-rotate-test.mjs +92 -0
  253. package/scripts/topo-avatar-rotate-test.mjs +85 -0
  254. package/scripts/topo-avatar-scale-test.mjs +89 -0
  255. package/scripts/topo-badge-chat-gate-test.mjs +74 -0
  256. package/scripts/topo-brand-drop-shadow-test.mjs +71 -0
  257. package/scripts/topo-brand-logo-breath-test.mjs +102 -0
  258. package/scripts/topo-brand-logo-halo-layers-test.mjs +85 -0
  259. package/scripts/topo-brand-logo-hover-brightness-test.mjs +105 -0
  260. package/scripts/topo-brand-logo-hover-rotate-test.mjs +93 -0
  261. package/scripts/topo-brand-logo-hover-test.mjs +85 -0
  262. package/scripts/topo-canvas-desc-a11y-test.mjs +87 -0
  263. package/scripts/topo-canvas-entrance-animation-test.mjs +117 -0
  264. package/scripts/topo-canvas-scan-beam-diagonal-test.mjs +148 -0
  265. package/scripts/topo-canvas-scan-beam-test.mjs +109 -0
  266. package/scripts/topo-canvas-scan-beam-vertical-test.mjs +123 -0
  267. package/scripts/topo-card-chat-brightness-test.mjs +86 -0
  268. package/scripts/topo-chat-ring-breath-curve-test.mjs +114 -0
  269. package/scripts/topo-chat-ring-brightness-test.mjs +80 -0
  270. package/scripts/topo-chat-ring-halo-layers-test.mjs +100 -0
  271. package/scripts/topo-chat-ring-r-breath-test.mjs +121 -0
  272. package/scripts/topo-chat-ring-status-halo-test.mjs +106 -0
  273. package/scripts/topo-chat-ring-sw-breath-test.mjs +123 -0
  274. package/scripts/topo-chip-pin-halo-test.mjs +85 -0
  275. package/scripts/topo-chip-row-digit-ls-test.mjs +135 -0
  276. package/scripts/topo-chip-row-member-alias-lit-test.mjs +154 -0
  277. package/scripts/topo-chip-row-tier-glow-brightness-test.mjs +99 -0
  278. package/scripts/topo-chip-row-unit-hover-tracking-test.mjs +124 -0
  279. package/scripts/topo-chrome-control-halo-layers-test.mjs +22 -0
  280. package/scripts/topo-chrome-fullscreen-breath-test.mjs +121 -0
  281. package/scripts/topo-chrome-layout-trailer-breath-test.mjs +86 -0
  282. package/scripts/topo-chrome-nodesize-trailer-breath-test.mjs +86 -0
  283. package/scripts/topo-chrome-reset-breath-test.mjs +115 -0
  284. package/scripts/topo-chrome-strip-entrance-animation-test.mjs +115 -0
  285. package/scripts/topo-chrome-wrapper-halo-test.mjs +83 -0
  286. package/scripts/topo-chrome-zoom-wrapper-breath-test.mjs +85 -0
  287. package/scripts/topo-click-ripple-glow-test.mjs +86 -0
  288. package/scripts/topo-click-ripple-halo-layers-test.mjs +79 -0
  289. package/scripts/topo-click-ripple-sw-test.mjs +110 -0
  290. package/scripts/topo-cluster-count-attr-test.mjs +80 -0
  291. package/scripts/topo-crescent-breath-test.mjs +104 -0
  292. package/scripts/topo-crescent-envelope-breath-test.mjs +103 -0
  293. package/scripts/topo-crescent-recede-test.mjs +111 -0
  294. package/scripts/topo-dense-alias-chat-halo-test.mjs +73 -0
  295. package/scripts/topo-dense-alias-halo-layers-test.mjs +80 -0
  296. package/scripts/topo-dual-axis-surfaces-catalog-test.mjs +94 -0
  297. package/scripts/topo-edge-badge-circle-brightness-test.mjs +82 -0
  298. package/scripts/topo-edge-badge-circle-hot-pulse-test.mjs +100 -0
  299. package/scripts/topo-edge-badge-digit-halo-layers-test.mjs +107 -0
  300. package/scripts/topo-edge-badge-endpoint-gate-test.mjs +94 -0
  301. package/scripts/topo-edge-badge-halo-layers-test.mjs +85 -0
  302. package/scripts/topo-edge-badge-hot-pulse-test.mjs +92 -0
  303. package/scripts/topo-edge-badge-hover-glow-test.mjs +90 -0
  304. package/scripts/topo-edge-badge-text-brightness-test.mjs +83 -0
  305. package/scripts/topo-edge-chat-gate-test.mjs +71 -0
  306. package/scripts/topo-edge-flow-rail-halo-layers-test.mjs +89 -0
  307. package/scripts/topo-edge-particle-brightness-test.mjs +82 -0
  308. package/scripts/topo-edge-particle-halo-layers-test.mjs +91 -0
  309. package/scripts/topo-edge-pill-glow-test.mjs +67 -0
  310. package/scripts/topo-edge-pin-halo-test.mjs +99 -0
  311. package/scripts/topo-edge-visible-brightness-test.mjs +84 -0
  312. package/scripts/topo-edge-visible-halo-layers-test.mjs +87 -0
  313. package/scripts/topo-endpoint-ring-brightness-test.mjs +83 -0
  314. package/scripts/topo-endpoint-ring-flow-halo-test.mjs +107 -0
  315. package/scripts/topo-endpoint-ring-halo-layers-test.mjs +100 -0
  316. package/scripts/topo-filter-pill-glow-test.mjs +90 -0
  317. package/scripts/topo-filter-pill-halo-layers-test.mjs +27 -0
  318. package/scripts/topo-flow-arrow-brightness-test.mjs +82 -0
  319. package/scripts/topo-flow-rail-brightness-test.mjs +80 -0
  320. package/scripts/topo-fullscreen-attr-test.mjs +73 -0
  321. package/scripts/topo-fullscreen-brightness-test.mjs +84 -0
  322. package/scripts/topo-fullscreen-icon-rotate-test.mjs +93 -0
  323. package/scripts/topo-grid-content-bottom-attr-test.mjs +72 -0
  324. package/scripts/topo-group-box-brightness-test.mjs +84 -0
  325. package/scripts/topo-group-box-halo-layers-test.mjs +91 -0
  326. package/scripts/topo-group-chat-gate-test.mjs +77 -0
  327. package/scripts/topo-group-label-brightness-test.mjs +84 -0
  328. package/scripts/topo-group-label-halo-layers-test.mjs +78 -0
  329. package/scripts/topo-group-label-hover-glow-test.mjs +86 -0
  330. package/scripts/topo-group-label-member-alias-hover-test.mjs +125 -0
  331. package/scripts/topo-group-pill-glow-test.mjs +76 -0
  332. package/scripts/topo-group-tint-brightness-test.mjs +82 -0
  333. package/scripts/topo-h2-dual-axis-breath-test.mjs +92 -0
  334. package/scripts/topo-h2-triple-axis-breath-test.mjs +142 -0
  335. package/scripts/topo-halo-chat-gate-test.mjs +72 -0
  336. package/scripts/topo-hover-detail-halo-test.mjs +76 -0
  337. package/scripts/topo-hub-a11y-title-test.mjs +95 -0
  338. package/scripts/topo-hub-core-brightness-test.mjs +82 -0
  339. package/scripts/topo-hub-core-halo-layers-test.mjs +81 -0
  340. package/scripts/topo-hub-digit-brightness-test.mjs +79 -0
  341. package/scripts/topo-hub-digit-halo-layers-test.mjs +76 -0
  342. package/scripts/topo-hub-digit-ls-test.mjs +119 -0
  343. package/scripts/topo-hub-halo-brightness-test.mjs +80 -0
  344. package/scripts/topo-hub-halo-glow-test.mjs +96 -0
  345. package/scripts/topo-hub-halo-halo-layers-test.mjs +76 -0
  346. package/scripts/topo-hub-highlight-brightness-test.mjs +84 -0
  347. package/scripts/topo-hub-highlight-glow-test.mjs +99 -0
  348. package/scripts/topo-hub-highlight-halo-layers-test.mjs +78 -0
  349. package/scripts/topo-hub-highlight-r-test.mjs +112 -0
  350. package/scripts/topo-hub-hover-ring-brightness-test.mjs +79 -0
  351. package/scripts/topo-hub-hover-ring-glow-test.mjs +97 -0
  352. package/scripts/topo-hub-hover-ring-halo-layers-test.mjs +71 -0
  353. package/scripts/topo-hub-spoke-brightness-test.mjs +77 -0
  354. package/scripts/topo-hub-spoke-glow-test.mjs +112 -0
  355. package/scripts/topo-hub-spoke-halo-layers-test.mjs +97 -0
  356. package/scripts/topo-hub-spoke-self-filter-test.mjs +119 -0
  357. package/scripts/topo-kicker-breath-test.mjs +100 -0
  358. package/scripts/topo-kicker-dual-axis-breath-test.mjs +81 -0
  359. package/scripts/topo-kicker-halo-layers-test.mjs +82 -0
  360. package/scripts/topo-kicker-triple-axis-breath-test.mjs +124 -0
  361. package/scripts/topo-label-card-brightness-test.mjs +81 -0
  362. package/scripts/topo-layout-hover-fw-test.mjs +98 -0
  363. package/scripts/topo-layout-toggle-brightness-test.mjs +94 -0
  364. package/scripts/topo-layout-toggle-halo-layers-test.mjs +95 -0
  365. package/scripts/topo-legend-count-brightness-test.mjs +80 -0
  366. package/scripts/topo-legend-count-halo-layers-test.mjs +79 -0
  367. package/scripts/topo-legend-count-letter-spacing-test.mjs +108 -0
  368. package/scripts/topo-legend-label-fw-test.mjs +107 -0
  369. package/scripts/topo-legend-panel-title-breath-test.mjs +86 -0
  370. package/scripts/topo-legend-pin-ring-brightness-test.mjs +82 -0
  371. package/scripts/topo-legend-pin-ring-halo-layers-test.mjs +71 -0
  372. package/scripts/topo-legend-row-count-brightness-test.mjs +85 -0
  373. package/scripts/topo-legend-row-label-glow-test.mjs +102 -0
  374. package/scripts/topo-legend-swatch-glow-test.mjs +109 -0
  375. package/scripts/topo-legend-swatch-member-alias-match-test.mjs +139 -0
  376. package/scripts/topo-legend-tint-brightness-test.mjs +83 -0
  377. package/scripts/topo-legend-trio-halo-layers-test.mjs +22 -0
  378. package/scripts/topo-live-lanes.mjs +48 -0
  379. package/scripts/topo-minimap-container-halo-test.mjs +82 -0
  380. package/scripts/topo-minimap-dot-chat-gate-test.mjs +81 -0
  381. package/scripts/topo-minimap-hover-glow-test.mjs +109 -0
  382. package/scripts/topo-minimap-viewport-brightness-test.mjs +84 -0
  383. package/scripts/topo-minimap-viewport-halo-layers-test.mjs +24 -0
  384. package/scripts/topo-more-footer-brightness-test.mjs +94 -0
  385. package/scripts/topo-node-alias-brightness-test.mjs +84 -0
  386. package/scripts/topo-node-avatar-halo-layers-test.mjs +25 -0
  387. package/scripts/topo-node-hover-ring-halo-layers-test.mjs +70 -0
  388. package/scripts/topo-node-label-card-halo-test.mjs +76 -0
  389. package/scripts/topo-node-sub-text-brightness-test.mjs +88 -0
  390. package/scripts/topo-nodesize-brightness-test.mjs +82 -0
  391. package/scripts/topo-nodesize-halo-layers-test.mjs +89 -0
  392. package/scripts/topo-nodesize-hover-fw-test.mjs +99 -0
  393. package/scripts/topo-orphan-label-opacity-test.mjs +98 -0
  394. package/scripts/topo-overlap-test.mjs +22 -8
  395. package/scripts/topo-panel-count-halo-layers-test.mjs +91 -0
  396. package/scripts/topo-panel-count-hover-ls-test.mjs +87 -0
  397. package/scripts/topo-panel-rect-halo-test.mjs +90 -0
  398. package/scripts/topo-panel-row-brightness-test.mjs +116 -0
  399. package/scripts/topo-panel-title-brightness-test.mjs +98 -0
  400. package/scripts/topo-panel-title-glow-test.mjs +111 -0
  401. package/scripts/topo-panel-titles-dual-axis-breath-test.mjs +94 -0
  402. package/scripts/topo-panel-titles-halo-layers-test.mjs +23 -0
  403. package/scripts/topo-panel-titles-triple-axis-breath-test.mjs +161 -0
  404. package/scripts/topo-pill-x-rotate-test.mjs +96 -0
  405. package/scripts/topo-pip-brightness-test.mjs +85 -0
  406. package/scripts/topo-pressure-bar-halo-layers-test.mjs +19 -0
  407. package/scripts/topo-pressure-seg-glow-test.mjs +92 -0
  408. package/scripts/topo-pressure-seg-member-alias-match-test.mjs +133 -0
  409. package/scripts/topo-pressure-seg-motion-test.mjs +101 -0
  410. package/scripts/topo-r717-scan-beam-pair-pattern-test.mjs +100 -0
  411. package/scripts/topo-r717-triple-axis-pair-pattern-test.mjs +113 -0
  412. package/scripts/topo-r717-triple-axis-tier-pattern-test.mjs +117 -0
  413. package/scripts/topo-recent-count-brightness-test.mjs +84 -0
  414. package/scripts/topo-recent-more-fw-test.mjs +126 -0
  415. package/scripts/topo-recent-more-halo-layers-test.mjs +90 -0
  416. package/scripts/topo-recent-panel-hot-pulse-test.mjs +105 -0
  417. package/scripts/topo-recent-panel-title-breath-test.mjs +91 -0
  418. package/scripts/topo-recent-pip-halo-layers-test.mjs +82 -0
  419. package/scripts/topo-recent-row-chat-gate-test.mjs +75 -0
  420. package/scripts/topo-recent-row-content-lift-test.mjs +140 -0
  421. package/scripts/topo-recent-row-fw-test.mjs +115 -0
  422. package/scripts/topo-recent-row-text-glow-test.mjs +86 -0
  423. package/scripts/topo-recent-row-text-halo-layers-test.mjs +67 -0
  424. package/scripts/topo-recent-tint-brightness-test.mjs +80 -0
  425. package/scripts/topo-recent-ts-brightness-test.mjs +86 -0
  426. package/scripts/topo-reduced-motion-attr-test.mjs +69 -0
  427. package/scripts/topo-reset-brightness-test.mjs +83 -0
  428. package/scripts/topo-reset-icon-hover-scale-test.mjs +102 -0
  429. package/scripts/topo-respiratory-axis-count-stats-test.mjs +154 -0
  430. package/scripts/topo-respiratory-patterns-catalog-test.mjs +112 -0
  431. package/scripts/topo-respiratory-rolodex-test.mjs +83 -0
  432. package/scripts/topo-respiratory-tiers-catalog-test.mjs +119 -0
  433. package/scripts/topo-respiratory-triple-axis-surfaces-catalog-test.mjs +127 -0
  434. package/scripts/topo-runtime-badge-brightness-test.mjs +78 -0
  435. package/scripts/topo-runtime-badge-glow-test.mjs +108 -0
  436. package/scripts/topo-runtime-badge-halo-layers-test.mjs +87 -0
  437. package/scripts/topo-runtime-badge-rotate-test.mjs +85 -0
  438. package/scripts/topo-section-title-breath-test.mjs +83 -0
  439. package/scripts/topo-section-title-halo-layers-test.mjs +88 -0
  440. package/scripts/topo-spoke-chat-gate-test.mjs +72 -0
  441. package/scripts/topo-starfield-hue-test.mjs +109 -0
  442. package/scripts/topo-status-pin-pill-halo-layers-test.mjs +17 -0
  443. package/scripts/topo-status-ring-brightness-test.mjs +84 -0
  444. package/scripts/topo-status-ring-chat-gate-test.mjs +72 -0
  445. package/scripts/topo-status-ring-halo-layers-test.mjs +105 -0
  446. package/scripts/topo-status-ring-status-halo-test.mjs +110 -0
  447. package/scripts/topo-sub-text-chat-brightness-test.mjs +81 -0
  448. package/scripts/topo-svg-title-a11y-r731-test.mjs +93 -0
  449. package/scripts/topo-svg-title-a11y-test.mjs +88 -0
  450. package/scripts/topo-title-block-entrance-animation-test.mjs +127 -0
  451. package/scripts/topo-title-block-envelope-breath-test.mjs +87 -0
  452. package/scripts/topo-titleblock-h2-hover-fw-test.mjs +109 -0
  453. package/scripts/topo-titleblock-h2-hover-tracking-test.mjs +128 -0
  454. package/scripts/topo-titleblock-kicker-hover-test.mjs +134 -0
  455. package/scripts/topo-tree-diag.mjs +95 -0
  456. package/scripts/topo-vendor-chip-glow-test.mjs +97 -0
  457. package/scripts/topo-vendor-chip-halo-layers-test.mjs +18 -0
  458. package/scripts/topo-vendor-chip-pin-halo-test.mjs +88 -0
  459. package/scripts/topo-vendor-count-suffix-halo-layers-test.mjs +79 -0
  460. package/scripts/topo-vendor-distribution-wrapper-halo-test.mjs +93 -0
  461. package/scripts/topo-vendor-letter-halo-layers-test.mjs +93 -0
  462. package/scripts/topo-vendor-pill-glow-test.mjs +98 -0
  463. package/scripts/topo-watermark-breath-test.mjs +100 -0
  464. package/scripts/topo-watermark-dual-axis-breath-test.mjs +88 -0
  465. package/scripts/topo-watermark-envelope-breath-test.mjs +88 -0
  466. package/scripts/topo-watermark-recede-test.mjs +114 -0
  467. package/scripts/topo-watermark-triple-axis-breath-test.mjs +129 -0
  468. package/scripts/topo-working-online-chip-halo-layers-test.mjs +94 -0
  469. package/scripts/topo-zoom-buttons-brightness-test.mjs +94 -0
  470. package/scripts/topo-zoom-in-out-halo-layers-test.mjs +97 -0
  471. package/scripts/topo-zoom-level-breath-test.mjs +87 -0
  472. package/scripts/topo-zoom-level-brightness-test.mjs +83 -0
  473. package/scripts/topo-zoom-level-color-test.mjs +105 -0
  474. package/scripts/topo-zoom-level-dual-axis-breath-test.mjs +83 -0
  475. package/scripts/topo-zoom-level-halo-layers-test.mjs +78 -0
  476. package/scripts/topo-zoom-level-triple-axis-breath-test.mjs +148 -0
  477. package/.next/static/chunks/0fuba20p57-zo.js +0 -1
  478. package/.next/static/chunks/0m.1mvl~t.avc.css +0 -2
  479. package/.next/static/chunks/10cj-86disers.js +0 -1
  480. package/.next/static/chunks/158h2g7f-nxbt.js +0 -4
  481. /package/.next/server/chunks/{[externals]__11vad82._.js.map → 0ykm__next-internal_server_app_api_hub_upload_route_actions_03_eqnf.js.map} +0 -0
  482. /package/.next/static/{tQ1EtcEiS7ikgAoAyZWm7 → xq88BMF7fMUHWh10yaKTn}/_buildManifest.js +0 -0
  483. /package/.next/static/{tQ1EtcEiS7ikgAoAyZWm7 → xq88BMF7fMUHWh10yaKTn}/_clientMiddlewareManifest.js +0 -0
  484. /package/.next/static/{tQ1EtcEiS7ikgAoAyZWm7 → xq88BMF7fMUHWh10yaKTn}/_ssgManifest.js +0 -0
@@ -0,0 +1,93 @@
1
+ /* Round 731 — extend SVG <title> a11y children to 2 more decorative
2
+ * SVG elements: title-block brand logo + canvas-corner crescent mark.
3
+ * Continues R730's a11y sub-family thread.
4
+ *
5
+ * R730 + R731 a11y title coverage so far (5 elements):
6
+ * R730 watermark <text> "sleep2agi · brand watermark"
7
+ * R730 recent panel title "recent signal · activity panel title"
8
+ * R730 legend panel title "legend · status / vendor / runtime swatch panel"
9
+ * R731 title-block brand logo "sleep2agi · brand logo"
10
+ * R731 canvas-corner crescent "sleep2agi · canvas-corner brand mark"
11
+ *
12
+ * Canonical title format: "<surface-name> · <structural-role>".
13
+ *
14
+ * Assertions:
15
+ * - Brand logo <svg> has a <title> child with expected text
16
+ * - Canvas-corner crescent <g> has a <title> child with expected text
17
+ * - Both follow the canonical "<name> · <role>" format
18
+ * - R730 watermark title still present (regression)
19
+ * - R730 legend title still present (regression)
20
+ */
21
+ import { chromium } from 'playwright';
22
+ import { readFileSync } from 'node:fs';
23
+
24
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
25
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
26
+
27
+ const browser = await chromium.launch({ headless: true });
28
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
29
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
30
+ await ctx.addInitScript(() => {
31
+ try {
32
+ localStorage.setItem('anet-theme', 'cyber');
33
+ localStorage.setItem('anet-topo-layout', 'ring');
34
+ sessionStorage.setItem('anet_v3_auth', '1');
35
+ } catch {}
36
+ });
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) => ({
42
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli',
43
+ network_id: nid, project_dir: null,
44
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
45
+ });
46
+ await route.fulfill({ response: r, json: { ...b, sessions: [mk('a·1'), mk('a·2')] } });
47
+ });
48
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
49
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
50
+ const page = await ctx.newPage();
51
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
52
+ await page.waitForSelector('[data-topo-brand-logo]', { timeout: 15000, state: 'attached' });
53
+ await page.waitForTimeout(300);
54
+
55
+ const state = await page.evaluate(() => {
56
+ const brandLogo = document.querySelector('[data-topo-brand-logo]');
57
+ const canvasMark = document.querySelector('[data-topo-brand-canvas-mark]');
58
+ const watermark = document.querySelector('[data-topo-brand-watermark]');
59
+ const legend = document.querySelector('[data-legend-panel-title]');
60
+ /* Get DIRECT child <title> (not descendant) so we don't pick up
61
+ * titles from nested SVG elements inside the brand logo's mask defs. */
62
+ const directTitleText = (el) => {
63
+ if (!el) return null;
64
+ for (const child of el.children) {
65
+ if (child.tagName.toLowerCase() === 'title') return child.textContent;
66
+ }
67
+ return null;
68
+ };
69
+ return {
70
+ brand_logo_title: directTitleText(brandLogo),
71
+ canvas_mark_title: directTitleText(canvasMark),
72
+ watermark_title: directTitleText(watermark),
73
+ legend_title: directTitleText(legend),
74
+ };
75
+ });
76
+
77
+ await browser.close();
78
+
79
+ const canonicalFormat = (s) => typeof s === 'string' && /^[^·]+ · [^·]+$/.test(s);
80
+
81
+ const results = {
82
+ brand_logo_title_present: state.brand_logo_title === 'sleep2agi · brand logo',
83
+ canvas_mark_title_present: state.canvas_mark_title === 'sleep2agi · canvas-corner brand mark',
84
+ brand_logo_canonical_format: canonicalFormat(state.brand_logo_title),
85
+ canvas_mark_canonical_format: canonicalFormat(state.canvas_mark_title),
86
+ r730_watermark_title_kept: state.watermark_title === 'sleep2agi · brand watermark',
87
+ r730_legend_title_kept: state.legend_title === 'legend · status / vendor / runtime swatch panel',
88
+ };
89
+ const ok = Object.values(results).every(Boolean);
90
+ console.log(`${ok ? '✅' : '❌'} R731 SVG <title> a11y extension to brand logo + canvas-corner crescent:`,
91
+ JSON.stringify(results, null, 2),
92
+ `\n state: ${JSON.stringify(state)}`);
93
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,88 @@
1
+ /* Round 730 — SVG <title> a11y children on the 3 primary SVG text
2
+ * anchors (watermark, recent panel title, legend panel title). Pure
3
+ * additive a11y improvement: screen readers can now identify these
4
+ * decorative SVG elements via their accessible name. No visible chrome
5
+ * change (mouse tooltips don't fire because the elements have
6
+ * pointer-events: none), no overlap risk.
7
+ *
8
+ * Pivot round: first non-breath-family addition after the R721–R729
9
+ * triple-axis + meta-doc hexagon thread.
10
+ *
11
+ * Assertions:
12
+ * - Watermark <text> has a <title> child with expected text
13
+ * - Recent panel title (when rendered) has a <title> child
14
+ * - Legend panel title has a <title> child
15
+ * - Each title text matches the canonical format "<name> · <role>"
16
+ * - Existing breath animations on all 3 are preserved (regression)
17
+ */
18
+ import { chromium } from 'playwright';
19
+ import { readFileSync } from 'node:fs';
20
+
21
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
22
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
23
+
24
+ const browser = await chromium.launch({ headless: true });
25
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
26
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
27
+ await ctx.addInitScript(() => {
28
+ try {
29
+ localStorage.setItem('anet-theme', 'cyber');
30
+ localStorage.setItem('anet-topo-layout', 'ring');
31
+ sessionStorage.setItem('anet_v3_auth', '1');
32
+ } catch {}
33
+ });
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 mk = (alias) => ({
39
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli',
40
+ network_id: nid, project_dir: null,
41
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
42
+ });
43
+ await route.fulfill({ response: r, json: { ...b, sessions: [mk('a·1'), mk('a·2')] } });
44
+ });
45
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
46
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
47
+ const page = await ctx.newPage();
48
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
49
+ await page.waitForSelector('[data-topo-brand-watermark]', { timeout: 15000, state: 'attached' });
50
+ await page.waitForTimeout(300);
51
+
52
+ const state = await page.evaluate(() => {
53
+ const watermark = document.querySelector('[data-topo-brand-watermark]');
54
+ const recent = document.querySelector('[data-recent-panel-title]');
55
+ const legend = document.querySelector('[data-legend-panel-title]');
56
+ const titleOf = (el) => el?.querySelector('title')?.textContent ?? null;
57
+ const smilCount = (el) => el?.querySelectorAll('animate').length ?? 0;
58
+ return {
59
+ watermark_present: !!watermark,
60
+ watermark_title_text: titleOf(watermark),
61
+ watermark_smil_count: smilCount(watermark), // expect 2 (R519 opacity + R712 letter-spacing)
62
+ recent_present: !!recent,
63
+ recent_title_text: titleOf(recent),
64
+ recent_smil_count: smilCount(recent), // expect 2 (R700 opacity + R713 font-size)
65
+ legend_present: !!legend,
66
+ legend_title_text: titleOf(legend),
67
+ legend_smil_count: smilCount(legend), // expect 2 (R701 opacity + R713 font-size)
68
+ };
69
+ });
70
+
71
+ await browser.close();
72
+
73
+ const results = {
74
+ watermark_title_present: state.watermark_present && state.watermark_title_text === 'sleep2agi · brand watermark',
75
+ watermark_smil_preserved: state.watermark_smil_count === 2,
76
+ /* Recent panel renders conditionally on flowLinks; same conditional-
77
+ * presence pattern as R728. Treat as pass if not rendered, strict
78
+ * check otherwise. */
79
+ recent_title_present: !state.recent_present || state.recent_title_text === 'recent signal · activity panel title',
80
+ recent_smil_preserved: !state.recent_present || state.recent_smil_count === 2,
81
+ legend_title_present: state.legend_present && state.legend_title_text === 'legend · status / vendor / runtime swatch panel',
82
+ legend_smil_preserved: state.legend_present && state.legend_smil_count === 2,
83
+ };
84
+ const ok = Object.values(results).every(Boolean);
85
+ console.log(`${ok ? '✅' : '❌'} R730 SVG <title> a11y children on 3 SVG text anchors (watermark + recent + legend titles):`,
86
+ JSON.stringify(results, null, 2),
87
+ `\n state: ${JSON.stringify(state)}`);
88
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,127 @@
1
+ /* Round 742 — title-block entrance animation. 2nd member of the
2
+ * entrance family (after R740 canvas root). Staggered choreography:
3
+ * R740 canvas 0 → 600 ms scale 0.99 → 1, opacity 0.8 → 1
4
+ * R742 title-block 200 → 700 ms translateY -4 → 0, opacity 0.7 → 1
5
+ *
6
+ * Assertions:
7
+ * - .anet-topo-title-block-entrance class on title-block wrapper
8
+ * - data-topo-section-titleblock-entrance="true"
9
+ * - CSS @keyframes 0% translateY(-4px) opacity 0.7 → 100% translateY(0) opacity 1
10
+ * - .anet-topo-title-block-entrance binds 500ms ease-out 200ms delay
11
+ * - animation NOT infinite (runtime iteration count === '1')
12
+ * - prefers-reduced-motion guard present
13
+ * - Animation completes within ~900ms (200ms delay + 500ms + 200ms buffer)
14
+ * → after 900ms wait, computed transform is identity & opacity = 1
15
+ * - Existing R706 envelope-breath class still composed (regression)
16
+ * - R741 catalog one-shot-mount.members === 2, includes title-block in examples
17
+ * - R740 canvas entrance still present (regression)
18
+ */
19
+ import { chromium } from 'playwright';
20
+ import { readFileSync } from 'node:fs';
21
+
22
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
23
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
24
+
25
+ const browser = await chromium.launch({ headless: true });
26
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
27
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
28
+ await ctx.addInitScript(() => {
29
+ try {
30
+ localStorage.setItem('anet-theme', 'cyber');
31
+ localStorage.setItem('anet-topo-layout', 'ring');
32
+ sessionStorage.setItem('anet_v3_auth', '1');
33
+ } catch {}
34
+ });
35
+ await ctx.route('**/api/hub/status*', async (route) => {
36
+ const r = await route.fetch();
37
+ const b = await r.json();
38
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
39
+ const mk = (alias) => ({
40
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli',
41
+ network_id: nid, project_dir: null,
42
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
43
+ });
44
+ await route.fulfill({ response: r, json: { ...b, sessions: [mk('a·1'), mk('a·2')] } });
45
+ });
46
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
47
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
48
+ const page = await ctx.newPage();
49
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
50
+ await page.waitForSelector('[data-topo-section-titleblock-group]', { timeout: 15000, state: 'attached' });
51
+ /* Wait for both R740 (0-600ms) and R742 (200-700ms) animations to
52
+ * complete plus a safety buffer. */
53
+ await page.waitForTimeout(900);
54
+
55
+ const state = await page.evaluate(() => {
56
+ const tb = document.querySelector('[data-topo-section-titleblock-group]');
57
+ const svg = document.querySelector('[data-topo-canvas-aria]');
58
+ if (!tb) return null;
59
+ const cs = getComputedStyle(tb);
60
+ return {
61
+ has_entrance_class: tb.classList.contains('anet-topo-title-block-entrance'),
62
+ entrance_attr: tb.getAttribute('data-topo-section-titleblock-entrance'),
63
+ envelope_breath_kept: tb.classList.contains('anet-topo-title-block-envelope-breath'),
64
+ anim_name: cs.animationName,
65
+ anim_duration: cs.animationDuration,
66
+ anim_delay: cs.animationDelay,
67
+ anim_iteration: cs.animationIterationCount,
68
+ computed_transform: cs.transform,
69
+ computed_opacity: cs.opacity,
70
+ canvas_entrance_class: svg?.classList.contains('anet-topo-canvas-entrance') ?? null,
71
+ temporal_modes: svg?.getAttribute('data-topo-animation-temporal-modes') ?? null,
72
+ };
73
+ });
74
+
75
+ await browser.close();
76
+
77
+ const cssSrc = readFileSync('/home/vansin/agent-network-dashboard/app/globals.css', 'utf8');
78
+ const cssKfStart = /@keyframes anet-topo-title-block-entrance-kf\s*\{[\s\S]*?0%\s*\{[\s\S]*?transform:\s*translateY\(-4px\)[\s\S]*?opacity:\s*0\.7/.test(cssSrc);
79
+ const cssKfEnd = /@keyframes anet-topo-title-block-entrance-kf\s*\{[\s\S]*?100%\s*\{[\s\S]*?transform:\s*translateY\(0\)[\s\S]*?opacity:\s*1/.test(cssSrc);
80
+ const cssClassBound = /\.anet-topo-title-block-entrance\s*\{[\s\S]*?animation:\s*anet-topo-title-block-entrance-kf\s+500ms\s+ease-out\s+200ms\s+backwards/.test(cssSrc);
81
+ const cssReducedMotion = /@media\s*\(prefers-reduced-motion:\s*reduce\)\s*\{[\s\S]*?\.anet-topo-title-block-entrance\s*\{\s*animation:\s*none/.test(cssSrc);
82
+
83
+ const transformSettled = state?.computed_transform === 'none'
84
+ || state?.computed_transform === 'matrix(1, 0, 0, 1, 0, 0)';
85
+ /* The title-block carries TWO animations via the R742 compound:
86
+ * 1. entrance one-shot, 500ms — settles after ~700ms
87
+ * 2. envelope-breath infinite, 11s — opacity 0.92 ↔ 1 forever (R706)
88
+ * After the entrance completes, opacity is governed by the ongoing
89
+ * envelope breath, so it sits SOMEWHERE in [0.92, 1] — never exactly
90
+ * 1 at a random sample. Assert opacity ∈ [0.90, 1.00] (the R706
91
+ * breath band with a small tolerance) rather than === 1. */
92
+ const opacityInBreathBand = (() => {
93
+ const o = parseFloat(state?.computed_opacity ?? '0');
94
+ return o >= 0.90 && o <= 1.0;
95
+ })();
96
+ /* The compound animation's FIRST entry is the one-shot entrance;
97
+ * the SECOND is the infinite breath. Verify the entrance part is
98
+ * one-shot by checking the first iteration-count token === '1'. */
99
+ const entranceIsOneShot = (state?.anim_iteration ?? '').split(',')[0].trim() === '1';
100
+ const entranceIsFirstInCompound = (state?.anim_name ?? '').split(',')[0].trim() === 'anet-topo-title-block-entrance-kf';
101
+
102
+ let modes = null;
103
+ try { modes = JSON.parse(state?.temporal_modes ?? ''); } catch {}
104
+ const oneShot = Array.isArray(modes) ? modes.find(m => m.mode === 'one-shot-mount') : null;
105
+ const oneShotHas2 = oneShot?.members === 2 && Array.isArray(oneShot?.examples) && oneShot.examples.includes('title-block');
106
+
107
+ const results = {
108
+ has_entrance_class: state?.has_entrance_class === true,
109
+ entrance_attr_true: state?.entrance_attr === 'true',
110
+ envelope_breath_kept: state?.envelope_breath_kept === true,
111
+ css_keyframe_start: cssKfStart,
112
+ css_keyframe_end: cssKfEnd,
113
+ css_class_binds_500ms_delay_200: cssClassBound,
114
+ css_reduced_motion_guard: cssReducedMotion,
115
+ entrance_part_is_one_shot: entranceIsOneShot,
116
+ entrance_first_in_compound: entranceIsFirstInCompound,
117
+ transform_settled: transformSettled,
118
+ opacity_in_breath_band: opacityInBreathBand,
119
+ r740_canvas_entrance_kept: state?.canvas_entrance_class === true,
120
+ r741_one_shot_members_now_2: oneShotHas2,
121
+ };
122
+ const ok = Object.values(results).every(Boolean);
123
+ console.log(`${ok ? '✅' : '❌'} R742 title-block entrance animation (2nd one-shot member, staggered after canvas):`,
124
+ JSON.stringify(results, null, 2),
125
+ `\n state: ${JSON.stringify(state)}`,
126
+ `\n one-shot entry: ${JSON.stringify(oneShot)}`);
127
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,87 @@
1
+ /* Round 706 — title-block wrapper envelope breath at 11s. Mirrors the
2
+ * canvas-brand-pair nested-envelope pattern (R704/R705) at the title-
3
+ * block scope. Creates triply-nested respiration: 3 inner trio anchors
4
+ * (brand-logo 5s + kicker 6s + H2 10s) inside outer wrapper (11s).
5
+ *
6
+ * 11s coprime with all 3 inner cadences (5, 6, 10) — no common factor.
7
+ * Tighter alpha (0.92 ↔ 1, ~8%) than canvas-brand envelopes since
8
+ * title-block carries primary text identity.
9
+ */
10
+ import { chromium } from 'playwright';
11
+ import { readFileSync } from 'node:fs';
12
+
13
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
14
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
15
+
16
+ const browser = await chromium.launch({ headless: true });
17
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
18
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
19
+ await ctx.addInitScript(() => {
20
+ try {
21
+ localStorage.setItem('anet-theme', 'cyber');
22
+ localStorage.setItem('anet-topo-layout', 'ring');
23
+ sessionStorage.setItem('anet_v3_auth', '1');
24
+ } catch {}
25
+ });
26
+ await ctx.route('**/api/hub/status*', async (route) => {
27
+ const r = await route.fetch();
28
+ const b = await r.json();
29
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
30
+ const mk = (alias) => ({
31
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli',
32
+ network_id: nid, project_dir: null,
33
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
34
+ });
35
+ await route.fulfill({ response: r, json: { ...b, sessions: [mk('a·1'), mk('a·2')] } });
36
+ });
37
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
38
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
39
+ const page = await ctx.newPage();
40
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
41
+ await page.waitForSelector('[data-topo-section-titleblock-group]', { timeout: 15000, state: 'attached' });
42
+ await page.waitForTimeout(300);
43
+
44
+ const runtimeState = await page.evaluate(() => {
45
+ const wrap = document.querySelector('[data-topo-section-titleblock-group]');
46
+ if (!wrap) return null;
47
+ const cs = getComputedStyle(wrap);
48
+ return {
49
+ has_class: wrap.classList.contains('anet-topo-title-block-envelope-breath'),
50
+ breath_attr: wrap.getAttribute('data-topo-section-titleblock-envelope-breath'),
51
+ anim_name: cs.animationName,
52
+ anim_duration: cs.animationDuration,
53
+ anim_iter: cs.animationIterationCount,
54
+ };
55
+ });
56
+
57
+ await browser.close();
58
+
59
+ const cssSrc = readFileSync('/home/vansin/agent-network-dashboard/app/globals.css', 'utf8');
60
+ const tsxSrc = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
61
+
62
+ const cssKeyframes = /@keyframes anet-topo-title-block-envelope-breath-kf\s*\{[\s\S]*?0%, 100%\s*\{\s*opacity:\s*1;\s*\}[\s\S]*?50%\s*\{\s*opacity:\s*0\.92;/.test(cssSrc);
63
+ const cssRule = /\.anet-topo-title-block-envelope-breath\s*\{[\s\S]*?animation:\s*anet-topo-title-block-envelope-breath-kf\s+11s\s+ease-in-out\s+infinite/.test(cssSrc);
64
+ const cssReducedMotion = /prefers-reduced-motion:\s*reduce\s*\)\s*\{\s*\.anet-topo-title-block-envelope-breath\s*\{\s*animation:\s*none/.test(cssSrc);
65
+ const tsxClass = /anet-topo-title-block-envelope-breath group flex items-center gap-2\.5/.test(tsxSrc);
66
+ const tsxAttr = /data-topo-section-titleblock-envelope-breath="11s"/.test(tsxSrc);
67
+
68
+ const runtimeAnim = runtimeState?.anim_name === 'anet-topo-title-block-envelope-breath-kf' || runtimeState?.anim_name === 'none';
69
+ const runtimeDuration = runtimeState?.anim_duration === '11s' || runtimeState?.anim_name === 'none';
70
+
71
+ const results = {
72
+ wrapper_present: !!runtimeState,
73
+ has_breath_class: runtimeState?.has_class === true,
74
+ breath_attr_11s: runtimeState?.breath_attr === '11s',
75
+ runtime_anim_ok: runtimeAnim,
76
+ runtime_duration_ok: runtimeDuration,
77
+ css_keyframes: cssKeyframes,
78
+ css_rule: cssRule,
79
+ css_reduced_motion: cssReducedMotion,
80
+ tsx_class: tsxClass,
81
+ tsx_breath_attr: tsxAttr,
82
+ };
83
+ const ok = Object.values(results).every(Boolean);
84
+ console.log(`${ok ? '✅' : '❌'} R706 title-block wrapper envelope breath (11s, triply-nested respiration):`,
85
+ JSON.stringify(results, null, 2),
86
+ `\n runtime: ${JSON.stringify(runtimeState)}`);
87
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,109 @@
1
+ /* Round 556 verification: title-block H2 "Command mesh" gains
2
+ * a 2nd editorial-emphasis axis — group-hover:font-bold paired
3
+ * with R554's group-hover:tracking-tighter.
4
+ *
5
+ * Test phases:
6
+ * 1. rest: fontWeight = 600 (semibold), letter-spacing ≈ -0.45px
7
+ * 2. hover brand logo (sibling, propagates via group-hover):
8
+ * H2 fontWeight = 700 (bold), letter-spacing ≈ -0.9px
9
+ * 3. transition-property contains BOTH letter-spacing AND font-weight
10
+ * 4. source-side regex confirms group-hover:font-bold wired
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 fresh = new Date(Date.now() - 60 * 1000).toISOString();
17
+
18
+ const browser = await chromium.launch({ headless: true });
19
+ const ctx = await browser.newContext({ viewport: { width: 1500, 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
+ localStorage.setItem('anet-topo-layout', 'ring');
25
+ sessionStorage.setItem('anet_v3_auth', '1');
26
+ } catch {}
27
+ });
28
+ await ctx.route('**/api/hub/status*', async (route) => {
29
+ const r = await route.fetch();
30
+ const b = await r.json();
31
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
32
+ const mk = (alias) => ({
33
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli',
34
+ network_id: nid, project_dir: null,
35
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
36
+ });
37
+ await route.fulfill({ response: r, json: { ...b, sessions: [mk('a·1')] } });
38
+ });
39
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
40
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
41
+ const page = await ctx.newPage();
42
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
43
+ await page.waitForSelector('[data-topo-section-title]', { timeout: 15000 });
44
+ await page.waitForTimeout(500);
45
+
46
+ const h2Sel = '[data-topo-section-title]';
47
+ const logoSel = '[data-topo-brand-logo]';
48
+
49
+ const parsePx = (s) => parseFloat((s || '').replace(/px$/, ''));
50
+
51
+ // Phase 1: rest
52
+ const rest = await page.evaluate((s) => {
53
+ const el = document.querySelector(s);
54
+ if (!el) return null;
55
+ const cs = getComputedStyle(el);
56
+ return {
57
+ fontWeight: cs.fontWeight,
58
+ letterSpacing: cs.letterSpacing,
59
+ transitionProperty: cs.transitionProperty,
60
+ transitionDuration: cs.transitionDuration,
61
+ hoverFwAttr: el.getAttribute('data-topo-section-title-hover-fw'),
62
+ hoverTrackingAttr: el.getAttribute('data-topo-section-title-hover-tracking'),
63
+ };
64
+ }, h2Sel);
65
+
66
+ // Phase 2: hover brand logo (sibling) → group-hover propagates
67
+ await page.hover(logoSel);
68
+ await page.waitForTimeout(400);
69
+ const hover = await page.evaluate((s) => {
70
+ const el = document.querySelector(s);
71
+ if (!el) return null;
72
+ const cs = getComputedStyle(el);
73
+ return { fontWeight: cs.fontWeight, letterSpacing: cs.letterSpacing };
74
+ }, h2Sel);
75
+
76
+ await browser.close();
77
+
78
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
79
+ const sourceFwWired = /group-hover:font-bold/.test(src);
80
+ const sourceTransition = /transition-\[letter-spacing,font-weight\] duration-200 ease-out/.test(src);
81
+ const sourceFwAttr = /data-topo-section-title-hover-fw="700"/.test(src);
82
+
83
+ const restPx = parsePx(rest?.letterSpacing);
84
+ const hoverPx = parsePx(hover?.letterSpacing);
85
+
86
+ const results = {
87
+ rest_fw_semibold: rest?.fontWeight === '600',
88
+ rest_tracking_tight: Math.abs(restPx - (-0.45)) < 0.05,
89
+ hover_fw_bold: hover?.fontWeight === '700',
90
+ hover_tracking_tighter: Math.abs(hoverPx - (-0.9)) < 0.05,
91
+ // Both axes intensify together — confirm by directionality
92
+ fw_intensifies: parseInt(hover?.fontWeight || '0') > parseInt(rest?.fontWeight || '0'),
93
+ tracking_intensifies: hoverPx < restPx - 0.3, // more negative = tighter
94
+ transition_has_ls: /letter-spacing/.test(rest?.transitionProperty || ''),
95
+ transition_has_fw: /font-weight/.test(rest?.transitionProperty || ''),
96
+ transition_duration: rest?.transitionDuration === '0.2s',
97
+ rest_hover_fw_attr: rest?.hoverFwAttr === '700',
98
+ rest_hover_tracking_attr: rest?.hoverTrackingAttr === 'tracking-tighter',
99
+ source_fw_wired: sourceFwWired,
100
+ source_transition: sourceTransition,
101
+ source_fw_attr: sourceFwAttr,
102
+ };
103
+ const ok = Object.values(results).every(Boolean);
104
+ console.log(`${ok ? '✅' : '❌'} R556 title-block H2 group-hover:font-bold (2-axis editorial emphasis):`,
105
+ JSON.stringify(results, null, 2),
106
+ `\n rest: fw=${rest?.fontWeight}, ls=${rest?.letterSpacing}`,
107
+ `\n hover: fw=${hover?.fontWeight}, ls=${hover?.letterSpacing}`,
108
+ `\n transition: ${rest?.transitionProperty}`);
109
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,128 @@
1
+ /* Round 554 verification: title-block H2 "Command mesh" gains
2
+ * group-hover:tracking-tighter via the wrapper's `group` flag.
3
+ * Hovering the brand logo OR the title text should tighten the
4
+ * H2's kerning from -0.025em → -0.05em.
5
+ *
6
+ * Tailwind tracking utilities map to letter-spacing CSS:
7
+ * tracking-tight -0.025em
8
+ * tracking-tighter -0.05em
9
+ *
10
+ * At text-lg (18px font-size):
11
+ * tracking-tight = -0.45px
12
+ * tracking-tighter = -0.9px
13
+ *
14
+ * Test phases:
15
+ * 1. rest: H2 letter-spacing ≈ -0.45px (tracking-tight)
16
+ * 2. hover brand logo: H2 letter-spacing ≈ -0.9px (tracking-tighter)
17
+ * Verifies group-hover propagation from sibling element.
18
+ * 3. hover H2 itself: letter-spacing ≈ -0.9px (same)
19
+ * 4. source: wrapper has `group`, H2 has group-hover:tracking-tighter
20
+ */
21
+ import { chromium } from 'playwright';
22
+ import { readFileSync } from 'node:fs';
23
+
24
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
25
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
26
+
27
+ const browser = await chromium.launch({ headless: true });
28
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
29
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
30
+ await ctx.addInitScript(() => {
31
+ try {
32
+ localStorage.setItem('anet-theme', 'cyber');
33
+ localStorage.setItem('anet-topo-layout', 'ring');
34
+ sessionStorage.setItem('anet_v3_auth', '1');
35
+ } catch {}
36
+ });
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) => ({
42
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli',
43
+ network_id: nid, project_dir: null,
44
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
45
+ });
46
+ await route.fulfill({ response: r, json: { ...b, sessions: [mk('a·1')] } });
47
+ });
48
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
49
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
50
+ const page = await ctx.newPage();
51
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
52
+ await page.waitForSelector('[data-topo-section-title]', { timeout: 15000 });
53
+ await page.waitForTimeout(500);
54
+
55
+ const h2Sel = '[data-topo-section-title]';
56
+ const logoSel = '[data-topo-brand-logo]';
57
+
58
+ const parsePx = (s) => parseFloat((s || '').replace(/px$/, ''));
59
+
60
+ // Phase 1: rest
61
+ const rest = await page.evaluate((s) => {
62
+ const el = document.querySelector(s);
63
+ if (!el) return null;
64
+ const cs = getComputedStyle(el);
65
+ return {
66
+ letterSpacing: cs.letterSpacing,
67
+ transitionProperty: cs.transitionProperty,
68
+ transitionDuration: cs.transitionDuration,
69
+ fontSize: cs.fontSize,
70
+ hoverAttr: el.getAttribute('data-topo-section-title-hover-tracking'),
71
+ };
72
+ }, h2Sel);
73
+
74
+ // Phase 2: hover brand logo (different element, but same group → H2 should tighten)
75
+ await page.hover(logoSel);
76
+ await page.waitForTimeout(400);
77
+ const hoverLogo = await page.evaluate((s) => {
78
+ const el = document.querySelector(s);
79
+ if (!el) return null;
80
+ const cs = getComputedStyle(el);
81
+ return { letterSpacing: cs.letterSpacing };
82
+ }, h2Sel);
83
+
84
+ // Phase 3: hover H2 itself (cross-confirm)
85
+ await page.mouse.move(0, 0);
86
+ await page.waitForTimeout(300);
87
+ await page.hover(h2Sel);
88
+ await page.waitForTimeout(400);
89
+ const hoverH2 = await page.evaluate((s) => {
90
+ const el = document.querySelector(s);
91
+ if (!el) return null;
92
+ const cs = getComputedStyle(el);
93
+ return { letterSpacing: cs.letterSpacing };
94
+ }, h2Sel);
95
+
96
+ await browser.close();
97
+
98
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
99
+ const sourceGroupWrapper = /<div className="group flex items-center gap-2\.5" data-topo-section-titleblock-group>/.test(src);
100
+ const sourceH2HoverTracking = /group-hover:tracking-tighter/.test(src);
101
+ const sourceH2HoverAttr = /data-topo-section-title-hover-tracking="tracking-tighter"/.test(src);
102
+
103
+ // Rest ≈ -0.45px (-0.025em × 18px), hover ≈ -0.9px (-0.05em × 18px)
104
+ const restPx = parsePx(rest?.letterSpacing);
105
+ const hoverLogoPx = parsePx(hoverLogo?.letterSpacing);
106
+ const hoverH2Px = parsePx(hoverH2?.letterSpacing);
107
+
108
+ const results = {
109
+ rest_attr_present: rest?.hoverAttr === 'tracking-tighter',
110
+ rest_tracking_tight: Math.abs(restPx - (-0.45)) < 0.05,
111
+ hover_via_logo_tightens: Math.abs(hoverLogoPx - (-0.9)) < 0.05,
112
+ hover_via_h2_tightens: Math.abs(hoverH2Px - (-0.9)) < 0.05,
113
+ hover_tighter_than_rest: hoverLogoPx < restPx - 0.3,
114
+ transition_has_letter_spacing: /letter-spacing/.test(rest?.transitionProperty || ''),
115
+ transition_duration_200ms: rest?.transitionDuration === '0.2s',
116
+ source_group_wrapper: sourceGroupWrapper,
117
+ source_h2_group_hover: sourceH2HoverTracking,
118
+ source_h2_attr: sourceH2HoverAttr,
119
+ };
120
+ const ok = Object.values(results).every(Boolean);
121
+ console.log(`${ok ? '✅' : '❌'} R554 title-block H2 group-hover:tracking-tighter:`,
122
+ JSON.stringify(results, null, 2),
123
+ `\n rest letter-spacing: ${rest?.letterSpacing} (expect ≈ -0.45px)`,
124
+ `\n hover-via-logo: ${hoverLogo?.letterSpacing} (expect ≈ -0.9px)`,
125
+ `\n hover-via-h2: ${hoverH2?.letterSpacing} (expect ≈ -0.9px)`,
126
+ `\n font-size: ${rest?.fontSize}`,
127
+ `\n transition-property: ${rest?.transitionProperty}`);
128
+ process.exit(ok ? 0 : 1);