@sleep2agi/agent-network-dashboard 0.5.1-preview.13 → 0.5.1-preview.130

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 (274) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +3 -3
  3. package/.next/diagnostics/route-bundle-stats.json +7 -7
  4. package/.next/fallback-build-manifest.json +3 -3
  5. package/.next/prerender-manifest.json +3 -3
  6. package/.next/server/app/_global-error.html +1 -1
  7. package/.next/server/app/_global-error.rsc +1 -1
  8. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  9. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  14. package/.next/server/app/_not-found.html +2 -2
  15. package/.next/server/app/_not-found.rsc +2 -2
  16. package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  17. package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  18. package/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  19. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  20. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  21. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  22. package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
  23. package/.next/server/app/admin.html +2 -2
  24. package/.next/server/app/admin.rsc +2 -2
  25. package/.next/server/app/admin.segments/_full.segment.rsc +2 -2
  26. package/.next/server/app/admin.segments/_head.segment.rsc +1 -1
  27. package/.next/server/app/admin.segments/_index.segment.rsc +2 -2
  28. package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
  29. package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +1 -1
  30. package/.next/server/app/admin.segments/admin.segment.rsc +1 -1
  31. package/.next/server/app/index.html +2 -2
  32. package/.next/server/app/index.rsc +3 -3
  33. package/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  34. package/.next/server/app/index.segments/_full.segment.rsc +3 -3
  35. package/.next/server/app/index.segments/_head.segment.rsc +1 -1
  36. package/.next/server/app/index.segments/_index.segment.rsc +2 -2
  37. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  38. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  39. package/.next/server/app/login.html +2 -2
  40. package/.next/server/app/login.rsc +3 -3
  41. package/.next/server/app/login.segments/_full.segment.rsc +3 -3
  42. package/.next/server/app/login.segments/_head.segment.rsc +1 -1
  43. package/.next/server/app/login.segments/_index.segment.rsc +2 -2
  44. package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
  45. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +2 -2
  46. package/.next/server/app/login.segments/login.segment.rsc +1 -1
  47. package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
  48. package/.next/server/app/logs.html +2 -2
  49. package/.next/server/app/logs.rsc +2 -2
  50. package/.next/server/app/logs.segments/_full.segment.rsc +2 -2
  51. package/.next/server/app/logs.segments/_head.segment.rsc +1 -1
  52. package/.next/server/app/logs.segments/_index.segment.rsc +2 -2
  53. package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
  54. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +1 -1
  55. package/.next/server/app/logs.segments/logs.segment.rsc +1 -1
  56. package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
  57. package/.next/server/app/messages.html +2 -2
  58. package/.next/server/app/messages.rsc +2 -2
  59. package/.next/server/app/messages.segments/_full.segment.rsc +2 -2
  60. package/.next/server/app/messages.segments/_head.segment.rsc +1 -1
  61. package/.next/server/app/messages.segments/_index.segment.rsc +2 -2
  62. package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
  63. package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +1 -1
  64. package/.next/server/app/messages.segments/messages.segment.rsc +1 -1
  65. package/.next/server/app/node/page_client-reference-manifest.js +1 -1
  66. package/.next/server/app/node.html +2 -2
  67. package/.next/server/app/node.rsc +2 -2
  68. package/.next/server/app/node.segments/_full.segment.rsc +2 -2
  69. package/.next/server/app/node.segments/_head.segment.rsc +1 -1
  70. package/.next/server/app/node.segments/_index.segment.rsc +2 -2
  71. package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
  72. package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +1 -1
  73. package/.next/server/app/node.segments/node.segment.rsc +1 -1
  74. package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
  75. package/.next/server/app/nodes.html +2 -2
  76. package/.next/server/app/nodes.rsc +2 -2
  77. package/.next/server/app/nodes.segments/_full.segment.rsc +2 -2
  78. package/.next/server/app/nodes.segments/_head.segment.rsc +1 -1
  79. package/.next/server/app/nodes.segments/_index.segment.rsc +2 -2
  80. package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
  81. package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +1 -1
  82. package/.next/server/app/nodes.segments/nodes.segment.rsc +1 -1
  83. package/.next/server/app/page.js.nft.json +1 -1
  84. package/.next/server/app/page_client-reference-manifest.js +1 -1
  85. package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
  86. package/.next/server/app/server-logs.html +2 -2
  87. package/.next/server/app/server-logs.rsc +2 -2
  88. package/.next/server/app/server-logs.segments/_full.segment.rsc +2 -2
  89. package/.next/server/app/server-logs.segments/_head.segment.rsc +1 -1
  90. package/.next/server/app/server-logs.segments/_index.segment.rsc +2 -2
  91. package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
  92. package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +1 -1
  93. package/.next/server/app/server-logs.segments/server-logs.segment.rsc +1 -1
  94. package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
  95. package/.next/server/app/settings/networks.html +2 -2
  96. package/.next/server/app/settings/networks.rsc +2 -2
  97. package/.next/server/app/settings/networks.segments/_full.segment.rsc +2 -2
  98. package/.next/server/app/settings/networks.segments/_head.segment.rsc +1 -1
  99. package/.next/server/app/settings/networks.segments/_index.segment.rsc +2 -2
  100. package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
  101. package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +1 -1
  102. package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +1 -1
  103. package/.next/server/app/settings/networks.segments/settings.segment.rsc +1 -1
  104. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  105. package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
  106. package/.next/server/app/settings/tokens.html +2 -2
  107. package/.next/server/app/settings/tokens.rsc +2 -2
  108. package/.next/server/app/settings/tokens.segments/_full.segment.rsc +2 -2
  109. package/.next/server/app/settings/tokens.segments/_head.segment.rsc +1 -1
  110. package/.next/server/app/settings/tokens.segments/_index.segment.rsc +2 -2
  111. package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
  112. package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +1 -1
  113. package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +1 -1
  114. package/.next/server/app/settings/tokens.segments/settings.segment.rsc +1 -1
  115. package/.next/server/app/settings.html +2 -2
  116. package/.next/server/app/settings.rsc +3 -3
  117. package/.next/server/app/settings.segments/_full.segment.rsc +3 -3
  118. package/.next/server/app/settings.segments/_head.segment.rsc +1 -1
  119. package/.next/server/app/settings.segments/_index.segment.rsc +2 -2
  120. package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  121. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +2 -2
  122. package/.next/server/app/settings.segments/settings.segment.rsc +1 -1
  123. package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
  124. package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
  125. package/.next/server/app/tasks.html +2 -2
  126. package/.next/server/app/tasks.rsc +2 -2
  127. package/.next/server/app/tasks.segments/_full.segment.rsc +2 -2
  128. package/.next/server/app/tasks.segments/_head.segment.rsc +1 -1
  129. package/.next/server/app/tasks.segments/_index.segment.rsc +2 -2
  130. package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
  131. package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +1 -1
  132. package/.next/server/app/tasks.segments/tasks.segment.rsc +1 -1
  133. package/.next/server/chunks/ssr/{[root-of-the-server]__03b.f76._.js → [root-of-the-server]__0sv~g.o._.js} +2 -2
  134. package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js.map +1 -0
  135. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +3 -3
  136. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
  137. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
  138. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
  139. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
  140. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
  141. package/.next/server/middleware-build-manifest.js +3 -3
  142. package/.next/server/pages/404.html +2 -2
  143. package/.next/server/pages/500.html +1 -1
  144. package/.next/server/server-reference-manifest.js +1 -1
  145. package/.next/server/server-reference-manifest.json +1 -1
  146. package/.next/static/chunks/0-at-u3ty_q5z.js +1 -0
  147. package/.next/static/chunks/0a4hmfvj-81x5.css +2 -0
  148. package/.next/static/chunks/0kz7356-vy97a.js +1 -0
  149. package/.next/static/chunks/{03~~oirxz7~vc.js → 0phbmewz8hp6a.js} +1 -1
  150. package/.next/static/chunks/11xegdodzgys0.js +4 -0
  151. package/.next/trace +2 -2
  152. package/.next/trace-build +1 -1
  153. package/app/components/TopoGraph.tsx +2528 -186
  154. package/package.json +1 -1
  155. package/screenshots/v0.11.0-147/after.png +0 -0
  156. package/scripts/p0-147-screenshot.mjs +53 -0
  157. package/scripts/topo-active-links-chip-hover-lift-test.mjs +93 -0
  158. package/scripts/topo-chip-digit-fontweight-test.mjs +105 -0
  159. package/scripts/topo-chip-digit-hover-bold-test.mjs +94 -0
  160. package/scripts/topo-chip-row-group-hover-brighten-test.mjs +107 -0
  161. package/scripts/topo-chip-row-hover-lift-test.mjs +95 -0
  162. package/scripts/topo-chrome-button-hover-lift-test.mjs +94 -0
  163. package/scripts/topo-chrome-fullscreen-icon-sw-test.mjs +92 -0
  164. package/scripts/topo-chrome-reset-icon-sw-test.mjs +80 -0
  165. package/scripts/topo-chrome-segmented-radius-test.mjs +100 -0
  166. package/scripts/topo-chrome-zoom-icon-sw-test.mjs +90 -0
  167. package/scripts/topo-click-ripple-opacity-test.mjs +99 -0
  168. package/scripts/topo-dashboard-version-attr-test.mjs +69 -0
  169. package/scripts/topo-dense-alias-opacity-test.mjs +68 -0
  170. package/scripts/topo-edge-badge-digit-fw-test.mjs +103 -0
  171. package/scripts/topo-edge-badge-fontsize-test.mjs +90 -0
  172. package/scripts/topo-edge-badge-hover-opacity-test.mjs +94 -0
  173. package/scripts/topo-edge-badge-hover-stroke-test.mjs +92 -0
  174. package/scripts/topo-edge-badge-letter-spacing-test.mjs +86 -0
  175. package/scripts/topo-edge-badge-opacity-test.mjs +80 -0
  176. package/scripts/topo-edge-badge-pin-opacity-test.mjs +86 -0
  177. package/scripts/topo-edge-badge-stroke-test.mjs +92 -0
  178. package/scripts/topo-edge-endpoint-hover-sw-test.mjs +127 -0
  179. package/scripts/topo-edge-freshness-floor-test.mjs +99 -0
  180. package/scripts/topo-edge-particle-hover-r-test.mjs +113 -0
  181. package/scripts/topo-edge-particle-radius-test.mjs +76 -0
  182. package/scripts/topo-edge-visible-linecap-test.mjs +89 -0
  183. package/scripts/topo-endpoint-ring-r-hover-test.mjs +89 -0
  184. package/scripts/topo-filter-pill-hover-lift-test.mjs +101 -0
  185. package/scripts/topo-filter-pill-hover-opacity-test.mjs +110 -0
  186. package/scripts/topo-filter-pill-value-fw-test.mjs +88 -0
  187. package/scripts/topo-filter-pill-x-hover-scale-test.mjs +99 -0
  188. package/scripts/topo-flow-rail-hover-sw-test.mjs +113 -0
  189. package/scripts/topo-flow-rail-linecap-test.mjs +79 -0
  190. package/scripts/topo-freshness-chip-hierarchy-test.mjs +93 -0
  191. package/scripts/topo-freshness-chip-tabular-test.mjs +41 -0
  192. package/scripts/topo-freshness-floor-lift-test.mjs +92 -0
  193. package/scripts/topo-freshness-suffix-tabular-test.mjs +88 -0
  194. package/scripts/topo-fullscreen-icon-hover-scale-test.mjs +91 -0
  195. package/scripts/topo-group-box-geom-transition-test.mjs +110 -0
  196. package/scripts/topo-group-box-stroke-test.mjs +105 -0
  197. package/scripts/topo-group-label-count-fontweight-test.mjs +108 -0
  198. package/scripts/topo-group-label-count-fw-test.mjs +100 -0
  199. package/scripts/topo-group-label-fw-pin-test.mjs +99 -0
  200. package/scripts/topo-group-label-letter-spacing-test.mjs +113 -0
  201. package/scripts/topo-group-label-tint-geom-test.mjs +94 -0
  202. package/scripts/topo-group-label-tint-transition-test.mjs +97 -0
  203. package/scripts/topo-group-pip-fontsize-test.mjs +106 -0
  204. package/scripts/topo-hover-detail-body-fw-test.mjs +101 -0
  205. package/scripts/topo-hover-detail-model-fw-test.mjs +98 -0
  206. package/scripts/topo-hover-detail-opacity-test.mjs +98 -0
  207. package/scripts/topo-hover-detail-rx-test.mjs +81 -0
  208. package/scripts/topo-hub-core-fill-hover-test.mjs +85 -0
  209. package/scripts/topo-hub-digit-fontsize-test.mjs +86 -0
  210. package/scripts/topo-hub-digit-fw-hover-test.mjs +102 -0
  211. package/scripts/topo-hub-halo-light-trough-test.mjs +88 -0
  212. package/scripts/topo-hub-halo-r-hover-test.mjs +82 -0
  213. package/scripts/topo-hub-halo-radius-test.mjs +86 -0
  214. package/scripts/topo-hub-halo-trough-test.mjs +83 -0
  215. package/scripts/topo-hub-highlight-opacity-test.mjs +88 -0
  216. package/scripts/topo-hub-highlight-radius-test.mjs +90 -0
  217. package/scripts/topo-hub-hover-ring-opacity-test.mjs +96 -0
  218. package/scripts/topo-hub-hover-ring-stroke-test.mjs +86 -0
  219. package/scripts/topo-hub-spoke-hover-opacity-test.mjs +119 -0
  220. package/scripts/topo-hub-spoke-hover-sw-test.mjs +121 -0
  221. package/scripts/topo-hub-spoke-linecap-test.mjs +80 -0
  222. package/scripts/topo-label-card-opacity-hover-test.mjs +99 -0
  223. package/scripts/topo-layout-toggle-radius-test.mjs +87 -0
  224. package/scripts/topo-legend-count-active-opacity-test.mjs +102 -0
  225. package/scripts/topo-legend-count-pin-fw-test.mjs +90 -0
  226. package/scripts/topo-legend-label-fontweight-test.mjs +94 -0
  227. package/scripts/topo-legend-pin-ring-stroke-test.mjs +101 -0
  228. package/scripts/topo-legend-row-letter-spacing-test.mjs +101 -0
  229. package/scripts/topo-minimap-offline-opacity-test.mjs +90 -0
  230. package/scripts/topo-minimap-online-hover-opacity-test.mjs +92 -0
  231. package/scripts/topo-minimap-online-opacity-test.mjs +93 -0
  232. package/scripts/topo-minimap-online-radius-test.mjs +85 -0
  233. package/scripts/topo-minimap-viewport-linejoin-test.mjs +75 -0
  234. package/scripts/topo-minimap-viewport-opacity-test.mjs +96 -0
  235. package/scripts/topo-minimap-viewport-rx-test.mjs +85 -0
  236. package/scripts/topo-more-flows-fontweight-test.mjs +103 -0
  237. package/scripts/topo-node-alias-letter-spacing-test.mjs +112 -0
  238. package/scripts/topo-node-halo-hover-opacity-test.mjs +104 -0
  239. package/scripts/topo-node-halo-light-offline-test.mjs +80 -0
  240. package/scripts/topo-node-halo-offline-opacity-test.mjs +87 -0
  241. package/scripts/topo-node-label-card-rx-test.mjs +85 -0
  242. package/scripts/topo-node-pulse-peak-test.mjs +89 -0
  243. package/scripts/topo-node-pulse-trough-test.mjs +91 -0
  244. package/scripts/topo-node-sub-text-fw-test.mjs +75 -0
  245. package/scripts/topo-node-sub-text-letter-spacing-test.mjs +115 -0
  246. package/scripts/topo-panel-count-fw-hover-test.mjs +105 -0
  247. package/scripts/topo-panel-stroke-hover-test.mjs +110 -0
  248. package/scripts/topo-pressure-bar-height-test.mjs +92 -0
  249. package/scripts/topo-pressure-kicker-fontweight-test.mjs +76 -0
  250. package/scripts/topo-recent-pip-radius-2-test.mjs +72 -0
  251. package/scripts/topo-recent-pip-radius-test.mjs +76 -0
  252. package/scripts/topo-recent-row-content-opacity-test.mjs +81 -0
  253. package/scripts/topo-recent-row-count-pin-fw-test.mjs +106 -0
  254. package/scripts/topo-recent-row-letter-spacing-test.mjs +109 -0
  255. package/scripts/topo-recent-row-pip-hover-r-test.mjs +104 -0
  256. package/scripts/topo-recent-row-text-fontweight-test.mjs +90 -0
  257. package/scripts/topo-runtime-icon-hover-test.mjs +96 -0
  258. package/scripts/topo-spoke-active-opacity-test.mjs +104 -0
  259. package/scripts/topo-spoke-active-stroke-test.mjs +95 -0
  260. package/scripts/topo-spoke-idle-opacity-test.mjs +91 -0
  261. package/scripts/topo-status-ring-hover-sw-test.mjs +105 -0
  262. package/scripts/topo-vendor-chip-hover-lift-test.mjs +87 -0
  263. package/scripts/topo-vendor-glyph-fontweight-test.mjs +102 -0
  264. package/scripts/topo-vendor-letter-hover-scale-test.mjs +129 -0
  265. package/scripts/topo-vendor-suffix-hover-brighten-test.mjs +87 -0
  266. package/scripts/topo-zoom-level-hover-fw-test.mjs +95 -0
  267. package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js.map +0 -1
  268. package/.next/static/chunks/0is7s1ik8dy24.js +0 -4
  269. package/.next/static/chunks/0kt7qs66ra2.2.css +0 -2
  270. package/.next/static/chunks/0yl__lz0pv5_7.js +0 -1
  271. package/.next/static/chunks/1090s4vyw-jrc.js +0 -1
  272. /package/.next/static/{Fa8EZgmgYGwvcoaGKpSmo → ZPH3qUPz78tGunqc1tq8i}/_buildManifest.js +0 -0
  273. /package/.next/static/{Fa8EZgmgYGwvcoaGKpSmo → ZPH3qUPz78tGunqc1tq8i}/_clientMiddlewareManifest.js +0 -0
  274. /package/.next/static/{Fa8EZgmgYGwvcoaGKpSmo → ZPH3qUPz78tGunqc1tq8i}/_ssgManifest.js +0 -0
@@ -0,0 +1,97 @@
1
+ /* Round 459 verification: group-label tint hitbox rect transition
2
+ * cadence sync 150ms → 200ms to match codex preview.125 parent
3
+ * <text> transition list. The R107 hitbox rect carried legacy 150ms
4
+ * cadence; codex's #147 P1 lifted the parent label to 200ms across
5
+ * every axis. R459 closes the 50ms desync so tint + label flip as
6
+ * one motion-coherent unit on hover / pin / unpin.
7
+ *
8
+ * Contract:
9
+ * - every <rect data-group-label-tint-transition="200ms">
10
+ * renders inside the group cluster boundary (i.e. there's at
11
+ * least one when fixtures have prefix-group clusters)
12
+ * - source-file style string is 'fill 200ms ease-out, opacity
13
+ * 200ms ease-out' on the rect just below #147 Hero D pivot
14
+ * comment, NOT 'fill 150ms, opacity 150ms'
15
+ * - source-file the data attr `data-group-label-tint-transition`
16
+ * is wired
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', 'grid');
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, status) => ({
39
+ alias, status, 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: [
44
+ mk('alpha·1', 'working'),
45
+ mk('alpha·2', 'idle'),
46
+ mk('beta·1', 'working'),
47
+ mk('beta·2', 'idle'),
48
+ ] } });
49
+ });
50
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
51
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
52
+
53
+ const page = await ctx.newPage();
54
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
55
+ await page.waitForSelector('[data-group-label]', { timeout: 15000 });
56
+ await page.waitForTimeout(400);
57
+
58
+ const probe = await page.evaluate(() => {
59
+ const tints = [...document.querySelectorAll('[data-group-label-tint-transition]')];
60
+ return {
61
+ count: tints.length,
62
+ transitions: tints.map(t => ({
63
+ value: t.getAttribute('data-group-label-tint-transition'),
64
+ style: t.getAttribute('style') || '',
65
+ computedTransition: getComputedStyle(t).transition,
66
+ })),
67
+ };
68
+ });
69
+
70
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
71
+ const sourceWired200 = /data-group-label-tint-transition="200ms"[\s\S]{0,300}?fill 200ms ease-out, opacity 200ms ease-out/.test(src);
72
+ // negative: there must NOT be a legacy 150ms transition WITHIN the
73
+ // same JSX element as data-group-label-tinted (the hitbox rect).
74
+ // other elements may still legitimately use 150ms (e.g. recent-row,
75
+ // legend-row at lines 8597 / 9458 — those are NOT the hitbox).
76
+ const tintRectBlockMatch = src.match(/data-group-label-tinted=[\s\S]{0,2000}?\/>/);
77
+ const tintRectBlock = tintRectBlockMatch ? tintRectBlockMatch[0] : '';
78
+ const sourceNoLegacy150 = !!tintRectBlock && !tintRectBlock.includes('fill 150ms');
79
+
80
+ await browser.close();
81
+
82
+ const tintCountGe2 = probe.count >= 2;
83
+ const allDataAttr200 = probe.transitions.every(t => t.value === '200ms');
84
+ const allStyle200 = probe.transitions.every(t => /fill 200ms ease-out, opacity 200ms ease-out/.test(t.style));
85
+
86
+ const results = {
87
+ tint_rect_count_ge_2: tintCountGe2,
88
+ all_data_attr_200: allDataAttr200,
89
+ all_style_200ms: allStyle200,
90
+ source_wired_200: sourceWired200,
91
+ source_no_legacy_150: sourceNoLegacy150,
92
+ };
93
+ const ok = Object.values(results).every(Boolean);
94
+ console.log(`${ok ? '✅' : '❌'} group label tint transition 150→200:`, JSON.stringify(results),
95
+ '\n count:', probe.count,
96
+ '\n first tint style:', probe.transitions[0]?.style);
97
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,106 @@
1
+ /* Round 458 verification: status pip strip fontSize 11 → 8 + dx
2
+ * 8/4/4 → 6/3/3, to scale-match the new 9px label that N站牛/codex
3
+ * preview.125 shipped (Hero D #147 Option C: label fontSize 13→9,
4
+ * opacity 0.55 rest / 1 hover+pin, count tspan 11→8). R458 closes
5
+ * the pip-strip leg of the same family.
6
+ *
7
+ * Contract:
8
+ * - every <tspan data-group-pip="working|idle|offline">
9
+ * reports font-size '8' (down from '11')
10
+ * - source-file dx values for the 3 pips are '6'/'3'/'3'
11
+ * (down from '8'/'4'/'4')
12
+ * - parent <text data-group-label> font-size stays '9' (codex
13
+ * preview.125 baseline preserved — R458 is a follow-on, not a
14
+ * replacement)
15
+ * - count tspan font-size stays '8' (codex preview.125 baseline)
16
+ */
17
+ import { chromium } from 'playwright';
18
+ import { readFileSync } from 'node:fs';
19
+
20
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
21
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
22
+
23
+ const browser = await chromium.launch({ headless: true });
24
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
25
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
26
+ await ctx.addInitScript(() => {
27
+ try {
28
+ localStorage.setItem('anet-theme', 'cyber');
29
+ localStorage.setItem('anet-topo-layout', 'grid');
30
+ sessionStorage.setItem('anet_v3_auth', '1');
31
+ } catch {}
32
+ });
33
+ // Fixture: 2 prefix groups, each MIXED (so at least one pip
34
+ // renders — R319 drops pips when count===box.count).
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, status) => ({
40
+ alias, status, 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: [
45
+ mk('alpha·1', 'working'),
46
+ mk('alpha·2', 'idle'),
47
+ mk('alpha·3', 'offline'),
48
+ mk('beta·1', 'working'),
49
+ mk('beta·2', 'idle'),
50
+ ] } });
51
+ });
52
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
53
+ await ctx.route('**/api/hub/tasks*', (r) => r.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.waitForSelector('[data-group-label]', { timeout: 15000 });
58
+ await page.waitForTimeout(500);
59
+
60
+ const probe = await page.evaluate(() => {
61
+ const pips = [...document.querySelectorAll('[data-group-pip]')].map(t => ({
62
+ tier: t.getAttribute('data-group-pip'),
63
+ fontSize: t.getAttribute('font-size'),
64
+ text: t.textContent,
65
+ }));
66
+ const labels = [...document.querySelectorAll('[data-group-label]')].map(t => ({
67
+ key: t.getAttribute('data-group-label'),
68
+ fontSize: t.getAttribute('font-size'),
69
+ }));
70
+ const counts = [...document.querySelectorAll('[data-group-label-count]')].map(t => ({
71
+ key: t.getAttribute('data-group-label-count'),
72
+ fontSize: t.getAttribute('font-size'),
73
+ }));
74
+ return { pips, labels, counts };
75
+ });
76
+
77
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
78
+ // dx values for the 3 pips — the source file uses dx="6" then dx="3" twice
79
+ const dxWorking = /data-group-pip="working"[\s\S]{0,400}?dx="6"/.test(src) ||
80
+ /dx="6"[\s\S]{0,400}?data-group-pip="working"/.test(src);
81
+ const dxIdle = /data-group-pip="idle"[\s\S]{0,400}?dx="3"/.test(src) ||
82
+ /dx="3"[\s\S]{0,400}?data-group-pip="idle"/.test(src);
83
+ const dxOffline = /data-group-pip="offline"[\s\S]{0,400}?dx="3"/.test(src) ||
84
+ /dx="3"[\s\S]{0,400}?data-group-pip="offline"/.test(src);
85
+
86
+ await browser.close();
87
+
88
+ const allPipsAre8 = probe.pips.length >= 2 && probe.pips.every(p => p.fontSize === '8');
89
+ const labelStays9 = probe.labels.length >= 2 && probe.labels.every(l => l.fontSize === '9');
90
+ const countStays8 = probe.counts.length >= 2 && probe.counts.every(c => c.fontSize === '8');
91
+
92
+ const results = {
93
+ pips_count_ge_2: probe.pips.length >= 2,
94
+ pips_all_fontsize_8: allPipsAre8,
95
+ labels_stay_9: labelStays9,
96
+ counts_stay_8: countStays8,
97
+ dx_working_is_6: dxWorking,
98
+ dx_idle_is_3: dxIdle,
99
+ dx_offline_is_3: dxOffline,
100
+ };
101
+ const ok = Object.values(results).every(Boolean);
102
+ console.log(`${ok ? '✅' : '❌'} group pip fontSize scale-match:`, JSON.stringify(results),
103
+ '\n pips:', JSON.stringify(probe.pips),
104
+ '\n labels:', JSON.stringify(probe.labels),
105
+ '\n counts:', JSON.stringify(probe.counts));
106
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,101 @@
1
+ /* Round 388 verification: hover-detail panel body lines fw 400 → 500.
2
+ * Small-text fw lift family (6th anchor) after R363 recent-row
3
+ * alias, R364 legend-row label, R366 group-label count tspan, R368
4
+ * +N footer, R373 pressure-bar kicker. Lifts the three fontSize=9
5
+ * body lines (runtime, host, task) in the on-hover product card to
6
+ * font-weight=500 so they read consistently bolder against the
7
+ * cyber-theme backdrop.
8
+ *
9
+ * Contract:
10
+ * - On hover, the hover-detail card mounts and:
11
+ * * Each fontSize=9 body line has font-weight attr === '500'
12
+ * * Each carries data-topo-hover-detail-body-fw === '500'
13
+ * * Tier structure preserved:
14
+ * - y=16 vendor headline at fw=700 unchanged
15
+ * - y=32 model subhead has no fw attr (preserved)
16
+ * - Task line keeps opacity 0.7 (R388 doesn't touch it)
17
+ * - The 3 body lines render at fontSize=9 (R388 doesn't touch size)
18
+ *
19
+ * Fixture: 2 idle sessions so hover-detail mounts cleanly without
20
+ * the dense-layout >16-node gate. Computed font-weight is asserted
21
+ * to be '500' as the browser-canonical resolution of the SVG attr.
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 fresh = new Date(Date.now() - 60 * 1000).toISOString();
28
+
29
+ const browser = await chromium.launch({ headless: true });
30
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
31
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
32
+ await ctx.addInitScript(() => {
33
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } 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('alpha'), mk('beta') ] } });
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
+
49
+ const page = await ctx.newPage();
50
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
51
+ await page.waitForSelector('g[data-node]', { timeout: 15000 });
52
+ await page.waitForTimeout(300);
53
+ await page.hover('g[data-node]');
54
+ await page.waitForSelector('[data-topo-hover-detail]', { timeout: 5000 });
55
+ await page.waitForTimeout(300);
56
+
57
+ const probe = await page.evaluate(() => {
58
+ const card = document.querySelector('[data-topo-hover-detail]');
59
+ if (!card) return null;
60
+ const texts = Array.from(card.querySelectorAll('text'));
61
+ const lines = texts.map((t) => ({
62
+ y: t.getAttribute('y'),
63
+ fontSize: t.getAttribute('font-size'),
64
+ fwAttr: t.getAttribute('font-weight'),
65
+ fwData: t.getAttribute('data-topo-hover-detail-body-fw'),
66
+ fwComputed: getComputedStyle(t).fontWeight,
67
+ opacityAttr: t.getAttribute('opacity'),
68
+ }));
69
+ return { lines };
70
+ });
71
+
72
+ await browser.close();
73
+
74
+ const byY = Object.fromEntries((probe?.lines || []).map((l) => [l.y, l]));
75
+
76
+ const results = {
77
+ // y=16 vendor headline unchanged at fw=700
78
+ vendor_y16_fw700: byY['16']?.fwAttr === '700',
79
+ // y=32 model subhead — no R388 attrs (size 10 differentiation)
80
+ model_y32_size_10: byY['32']?.fontSize === '10',
81
+ model_y32_no_body_fw_attr: byY['32']?.fwData === null || byY['32']?.fwData === undefined,
82
+ // y=48 runtime body — R388 lifts to fw=500
83
+ runtime_y48_size_9: byY['48']?.fontSize === '9',
84
+ runtime_y48_fw500: byY['48']?.fwAttr === '500',
85
+ runtime_y48_data_500: byY['48']?.fwData === '500',
86
+ runtime_y48_computed_500: byY['48']?.fwComputed === '500',
87
+ // y=64 host body — R388 lifts to fw=500
88
+ host_y64_size_9: byY['64']?.fontSize === '9',
89
+ host_y64_fw500: byY['64']?.fwAttr === '500',
90
+ host_y64_data_500: byY['64']?.fwData === '500',
91
+ host_y64_computed_500: byY['64']?.fwComputed === '500',
92
+ // y=80 task body — R388 lifts to fw=500, opacity 0.7 preserved
93
+ task_y80_size_9: byY['80']?.fontSize === '9',
94
+ task_y80_fw500: byY['80']?.fwAttr === '500',
95
+ task_y80_data_500: byY['80']?.fwData === '500',
96
+ task_y80_opacity_07: byY['80']?.opacityAttr === '0.7',
97
+ };
98
+ const ok = Object.values(results).every(Boolean);
99
+ console.log(`${ok ? '✅' : '❌'} hover-detail body lines fw 400 → 500:`, JSON.stringify(results),
100
+ '\n lines:', JSON.stringify(probe?.lines, null, 2));
101
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,98 @@
1
+ /* Round 389 verification: hover-detail model line (y=32) fw 400 → 600.
2
+ * Closes the hover-detail typography 3-tier ladder by giving the
3
+ * subhead its own weight tier between R0 vendor label badge (fw=700)
4
+ * and R388 body lines (fw=500). Sibling to the chip-internal-
5
+ * hierarchy arc (R333-R341/R362/R369) which uses fw=600 for value
6
+ * lines.
7
+ *
8
+ * Contract:
9
+ * - On hover, the hover-detail card mounts and:
10
+ * * y=32 <text> font-weight attr === '600'
11
+ * * y=32 data-topo-hover-detail-model-fw === '600'
12
+ * * y=32 computed font-weight === '600'
13
+ * * y=32 fontSize === '10' (R389 doesn't change size)
14
+ * - Tier ladder preserved:
15
+ * * y=16 vendor fw=700 (R0 invariant)
16
+ * * y=48/64/80 body fw=500 (R388 invariant)
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: 1500 } });
26
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
27
+ await ctx.addInitScript(() => {
28
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
29
+ });
30
+ await ctx.route('**/api/hub/status*', async (route) => {
31
+ const r = await route.fetch();
32
+ const b = await r.json();
33
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
34
+ const mk = (alias) => ({
35
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli',
36
+ network_id: nid, project_dir: null,
37
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
38
+ });
39
+ await route.fulfill({ response: r, json: { ...b, sessions: [ mk('alpha'), mk('beta') ] } });
40
+ });
41
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
42
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
43
+
44
+ const page = await ctx.newPage();
45
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
46
+ await page.waitForSelector('g[data-node]', { timeout: 15000 });
47
+ await page.waitForTimeout(300);
48
+ await page.hover('g[data-node]');
49
+ await page.waitForSelector('[data-topo-hover-detail]', { timeout: 5000 });
50
+ await page.waitForTimeout(300);
51
+
52
+ const probe = await page.evaluate(() => {
53
+ const card = document.querySelector('[data-topo-hover-detail]');
54
+ if (!card) return null;
55
+ const texts = Array.from(card.querySelectorAll('text'));
56
+ const get = (y) => {
57
+ const t = texts.find((el) => el.getAttribute('y') === y);
58
+ return t ? {
59
+ fontSize: t.getAttribute('font-size'),
60
+ fwAttr: t.getAttribute('font-weight'),
61
+ fwComputed: getComputedStyle(t).fontWeight,
62
+ modelFwData: t.getAttribute('data-topo-hover-detail-model-fw'),
63
+ bodyFwData: t.getAttribute('data-topo-hover-detail-body-fw'),
64
+ } : null;
65
+ };
66
+ return {
67
+ vendor: get('16'),
68
+ model: get('32'),
69
+ runtime: get('48'),
70
+ host: get('64'),
71
+ task: get('80'),
72
+ };
73
+ });
74
+
75
+ await browser.close();
76
+
77
+ const results = {
78
+ // R389 changes: y=32 model lifted to fw=600
79
+ model_y32_size_10: probe?.model?.fontSize === '10',
80
+ model_y32_fw_attr_600: probe?.model?.fwAttr === '600',
81
+ model_y32_data_600: probe?.model?.modelFwData === '600',
82
+ model_y32_computed_600: probe?.model?.fwComputed === '600',
83
+ // R0 invariant: y=16 vendor fw=700 unchanged
84
+ vendor_y16_fw_700: probe?.vendor?.fwAttr === '700',
85
+ // R388 invariants: body lines y=48/64/80 fw=500 unchanged
86
+ runtime_y48_fw_500: probe?.runtime?.fwAttr === '500',
87
+ host_y64_fw_500: probe?.host?.fwAttr === '500',
88
+ task_y80_fw_500: probe?.task?.fwAttr === '500',
89
+ // Tier-isolation invariant: only y=32 carries the model-fw data attr
90
+ vendor_y16_no_model_attr: probe?.vendor?.modelFwData === null || probe?.vendor?.modelFwData === undefined,
91
+ runtime_y48_no_model_attr: probe?.runtime?.modelFwData === null || probe?.runtime?.modelFwData === undefined,
92
+ host_y64_no_model_attr: probe?.host?.modelFwData === null || probe?.host?.modelFwData === undefined,
93
+ task_y80_no_model_attr: probe?.task?.modelFwData === null || probe?.task?.modelFwData === undefined,
94
+ };
95
+ const ok = Object.values(results).every(Boolean);
96
+ console.log(`${ok ? '✅' : '❌'} hover-detail model line fw 400 → 600:`, JSON.stringify(results),
97
+ '\n probe:', JSON.stringify(probe, null, 2));
98
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,98 @@
1
+ /* Round 387 verification: hover-detail panel cyber rest opacity
2
+ * 0.94 → 0.97. Theme-consistency / canvas-presence polish family
3
+ * (5th anchor) after R370 hub-ring 0.7→0.8 cyber, R371 edge-badge
4
+ * 0.82→0.85 cyber, R372 minimap offline 0.5→0.6, R386 hub-highlight
5
+ * idle 0.9→0.95. Unifies the active-hover product card's cyber
6
+ * backdrop with the R348 recent-signal/legend panel HOVER state
7
+ * (0.97 cyber) so all active-hover panels paint with the same
8
+ * confident backdrop.
9
+ *
10
+ * Contract:
11
+ * - On hover, the hover-detail panel mounts and:
12
+ * * <rect> opacity attr === '0.97' (cyber theme)
13
+ * * data-topo-hover-detail-opacity === '0.97' (cyber theme)
14
+ * - Pre-R387 invariants preserved:
15
+ * * <rect> rx === '8'
16
+ * * <rect> stroke is non-empty (pal.legendAccent)
17
+ * * data-topo-hover-detail is present (alias-keyed)
18
+ * * R348 drop-shadow filter is present
19
+ * - Light theme stays at opacity 0.98 (separately tested below).
20
+ *
21
+ * Fixture: 2 idle sessions so hover-detail mounts cleanly without
22
+ * the dense-layout >16-node gate (R387 inherits the same gate).
23
+ */
24
+ import { chromium } from 'playwright';
25
+ import { readFileSync } from 'node:fs';
26
+
27
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
28
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
29
+
30
+ async function probeWith(theme) {
31
+ const browser = await chromium.launch({ headless: true });
32
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
33
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
34
+ await ctx.addInitScript((t) => {
35
+ try { localStorage.setItem('anet-theme', t); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
36
+ }, theme);
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('alpha'), mk('beta') ] } });
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: 'domcontentloaded' });
52
+ await page.waitForSelector('g[data-node]', { timeout: 15000 });
53
+ await page.waitForTimeout(300);
54
+ // Hover the first node to mount hover-detail
55
+ await page.hover('g[data-node]');
56
+ await page.waitForSelector('[data-topo-hover-detail]', { timeout: 5000 });
57
+ await page.waitForTimeout(300);
58
+ const probe = await page.evaluate(() => {
59
+ const card = document.querySelector('[data-topo-hover-detail]');
60
+ if (!card) return null;
61
+ const rect = card.querySelector('rect');
62
+ return {
63
+ alias: card.getAttribute('data-topo-hover-detail'),
64
+ opacityAttr: rect?.getAttribute('opacity') ?? null,
65
+ opacityData: rect?.getAttribute('data-topo-hover-detail-opacity') ?? null,
66
+ rxAttr: rect?.getAttribute('rx') ?? null,
67
+ strokeAttr: rect?.getAttribute('stroke') ?? null,
68
+ filterStyle: rect?.getAttribute('style') ?? '',
69
+ };
70
+ });
71
+ await browser.close();
72
+ return probe;
73
+ }
74
+
75
+ const cyber = await probeWith('cyber');
76
+ const light = await probeWith('light');
77
+
78
+ const results = {
79
+ // Cyber: lifted from 0.94 → 0.97
80
+ cyber_opacity_attr_0_97: cyber?.opacityAttr === '0.97',
81
+ cyber_opacity_data_0_97: cyber?.opacityData === '0.97',
82
+ // Light: stays at 0.98 (R387 doesn't touch light)
83
+ light_opacity_attr_0_98: light?.opacityAttr === '0.98',
84
+ light_opacity_data_0_98: light?.opacityData === '0.98',
85
+ // Pre-R387 invariants on both themes
86
+ cyber_rx_8: cyber?.rxAttr === '8',
87
+ light_rx_8: light?.rxAttr === '8',
88
+ cyber_stroke_present: !!cyber?.strokeAttr && cyber.strokeAttr !== 'none',
89
+ light_stroke_present: !!light?.strokeAttr && light.strokeAttr !== 'none',
90
+ cyber_drop_shadow: cyber?.filterStyle.includes('drop-shadow'),
91
+ light_drop_shadow: light?.filterStyle.includes('drop-shadow'),
92
+ cyber_alias_present: !!cyber?.alias && cyber.alias.length > 0,
93
+ };
94
+ const ok = Object.values(results).every(Boolean);
95
+ console.log(`${ok ? '✅' : '❌'} hover-detail panel cyber opacity 0.94 → 0.97:`, JSON.stringify(results),
96
+ '\n cyber:', cyber,
97
+ '\n light:', light);
98
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,81 @@
1
+ /* Round 390 verification: hover-detail card rx 8 → 10. Aligns the
2
+ * card with the R331 panel tier (recent-signal, legend at rx=10)
3
+ * instead of the R332/R375/R376 compact-chrome / segmented-control
4
+ * tier (rx=8). Pure paint change — no layout shift, no bbox change.
5
+ *
6
+ * Contract:
7
+ * - On hover, the hover-detail card mounts and:
8
+ * * <rect> rx attr === '10'
9
+ * * data-topo-hover-detail-rx === '10'
10
+ * * Stays clear of the R332 minimap viewport (different element)
11
+ * - Pre-R390 invariants preserved:
12
+ * * R348 drop-shadow filter present
13
+ * * R387 opacity attr === '0.97' (cyber)
14
+ * * stroke present (pal.legendAccent)
15
+ * * width / height (192 / 88) unchanged
16
+ */
17
+ import { chromium } from 'playwright';
18
+ import { readFileSync } from 'node:fs';
19
+
20
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
21
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
22
+
23
+ const browser = await chromium.launch({ headless: true });
24
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
25
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
26
+ await ctx.addInitScript(() => {
27
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
28
+ });
29
+ await ctx.route('**/api/hub/status*', async (route) => {
30
+ const r = await route.fetch();
31
+ const b = await r.json();
32
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
33
+ const mk = (alias) => ({
34
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli',
35
+ network_id: nid, project_dir: null,
36
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
37
+ });
38
+ await route.fulfill({ response: r, json: { ...b, sessions: [ mk('alpha'), mk('beta') ] } });
39
+ });
40
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
41
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
42
+
43
+ const page = await ctx.newPage();
44
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
45
+ await page.waitForSelector('g[data-node]', { timeout: 15000 });
46
+ await page.waitForTimeout(300);
47
+ await page.hover('g[data-node]');
48
+ await page.waitForSelector('[data-topo-hover-detail]', { timeout: 5000 });
49
+ await page.waitForTimeout(300);
50
+
51
+ const probe = await page.evaluate(() => {
52
+ const card = document.querySelector('[data-topo-hover-detail]');
53
+ if (!card) return null;
54
+ const rect = card.querySelector('rect');
55
+ return {
56
+ rxAttr: rect?.getAttribute('rx') ?? null,
57
+ rxData: rect?.getAttribute('data-topo-hover-detail-rx') ?? null,
58
+ opacityAttr: rect?.getAttribute('opacity') ?? null,
59
+ widthAttr: rect?.getAttribute('width') ?? null,
60
+ heightAttr: rect?.getAttribute('height') ?? null,
61
+ strokeAttr: rect?.getAttribute('stroke') ?? null,
62
+ styleAttr: rect?.getAttribute('style') ?? '',
63
+ };
64
+ });
65
+
66
+ await browser.close();
67
+
68
+ const results = {
69
+ // R390: rx 8 → 10
70
+ rx_attr_10: probe?.rxAttr === '10',
71
+ rx_data_10: probe?.rxData === '10',
72
+ // Pre-R390 invariants preserved
73
+ opacity_cyber_0_97: probe?.opacityAttr === '0.97', // R387 invariant
74
+ width_192: probe?.widthAttr === '192',
75
+ height_88: probe?.heightAttr === '88',
76
+ stroke_present: !!probe?.strokeAttr && probe.strokeAttr !== 'none',
77
+ drop_shadow_present: probe?.styleAttr.includes('drop-shadow'),
78
+ };
79
+ const ok = Object.values(results).every(Boolean);
80
+ console.log(`${ok ? '✅' : '❌'} hover-detail card rx 8 → 10:`, JSON.stringify(results), '\n probe:', probe);
81
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,85 @@
1
+ /* Round 441 verification: hub center core fill brighten on hoveredHub.
2
+ * Cyber emerald-500 #10b981 → emerald-400 #34d399.
3
+ *
4
+ * Contract:
5
+ * - rest: core fill = '#10b981' + hovered='false'
6
+ * - hover the hub <g>: core fill = '#34d399' + hovered='true'
7
+ */
8
+ import { chromium } from 'playwright';
9
+ import { readFileSync } from 'node:fs';
10
+
11
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
12
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
13
+
14
+ const browser = await chromium.launch({ headless: true });
15
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
16
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
17
+ await ctx.addInitScript(() => {
18
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
19
+ });
20
+ await ctx.route('**/api/hub/status*', async (route) => {
21
+ const r = await route.fetch();
22
+ const b = await r.json();
23
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
24
+ const mk = (alias, status) => ({
25
+ alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
26
+ network_id: nid, project_dir: null,
27
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
28
+ });
29
+ await route.fulfill({ response: r, json: { ...b, sessions: [
30
+ mk('alpha', 'working'),
31
+ mk('beta', 'idle'),
32
+ ] } });
33
+ });
34
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
35
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
36
+
37
+ const page = await ctx.newPage();
38
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
39
+ await page.waitForSelector('[data-topo-hub-core]', { timeout: 15000 });
40
+ await page.waitForTimeout(400);
41
+
42
+ const readCore = () => page.evaluate(() => {
43
+ const c = document.querySelector('[data-topo-hub-core]');
44
+ return c ? {
45
+ fill_attr: c.getAttribute('fill'),
46
+ fill_data: c.getAttribute('data-topo-hub-core-fill'),
47
+ hovered: c.getAttribute('data-topo-hub-core-hovered'),
48
+ r: c.getAttribute('r'),
49
+ } : null;
50
+ });
51
+
52
+ const rest = await readCore();
53
+
54
+ const hubBox = await page.evaluate(() => {
55
+ const hub = document.querySelector('[data-topo-hub]');
56
+ if (!hub) return null;
57
+ const b = hub.getBoundingClientRect();
58
+ return { x: b.x + b.width / 2, y: b.y + b.height / 2 };
59
+ });
60
+ let hover = null;
61
+ if (hubBox) {
62
+ await page.mouse.move(hubBox.x, hubBox.y);
63
+ await page.waitForTimeout(300);
64
+ hover = await readCore();
65
+ await page.mouse.move(0, 0);
66
+ }
67
+
68
+ const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
69
+ const sourceWired = /const coreFill = isLight\s*\n\s*\?\s*\(isCoreHovered \? '#10b981' : '#059669'\)\s*\n\s*:\s*\(isCoreHovered \? '#34d399' : '#10b981'\)/.test(fileText);
70
+
71
+ await browser.close();
72
+
73
+ const results = {
74
+ rest_mounted: !!rest,
75
+ rest_fill_emerald_500: rest?.fill_data === '#10b981',
76
+ rest_hovered_false: rest?.hovered === 'false',
77
+ rest_r_10: rest?.r === '10',
78
+ hover_fill_emerald_400: hover?.fill_data === '#34d399',
79
+ hover_hovered_true: hover?.hovered === 'true',
80
+ source_wired: sourceWired,
81
+ };
82
+ const ok = Object.values(results).every(Boolean);
83
+ console.log(`${ok ? '✅' : '❌'} hub core fill hover:`, JSON.stringify(results),
84
+ '\n rest:', rest, '\n hover:', hover);
85
+ process.exit(ok ? 0 : 1);