@sleep2agi/agent-network-dashboard 0.5.3-preview.9 → 0.5.3-preview.91

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 (244) 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 +32 -32
  4. package/.next/fallback-build-manifest.json +3 -3
  5. package/.next/server/app/_global-error.html +1 -1
  6. package/.next/server/app/_global-error.rsc +1 -1
  7. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  8. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  9. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  12. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  13. package/.next/server/app/_not-found.html +2 -2
  14. package/.next/server/app/_not-found.rsc +12 -12
  15. package/.next/server/app/_not-found.segments/_full.segment.rsc +12 -12
  16. package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  17. package/.next/server/app/_not-found.segments/_index.segment.rsc +7 -7
  18. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  19. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  20. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  21. package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
  22. package/.next/server/app/admin.html +2 -2
  23. package/.next/server/app/admin.rsc +14 -14
  24. package/.next/server/app/admin.segments/_full.segment.rsc +14 -14
  25. package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
  26. package/.next/server/app/admin.segments/_index.segment.rsc +7 -7
  27. package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
  28. package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
  29. package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
  30. package/.next/server/app/index.html +2 -2
  31. package/.next/server/app/index.rsc +14 -14
  32. package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
  33. package/.next/server/app/index.segments/_full.segment.rsc +14 -14
  34. package/.next/server/app/index.segments/_head.segment.rsc +4 -4
  35. package/.next/server/app/index.segments/_index.segment.rsc +7 -7
  36. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  37. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  38. package/.next/server/app/login.html +2 -2
  39. package/.next/server/app/login.rsc +14 -14
  40. package/.next/server/app/login.segments/_full.segment.rsc +14 -14
  41. package/.next/server/app/login.segments/_head.segment.rsc +4 -4
  42. package/.next/server/app/login.segments/_index.segment.rsc +7 -7
  43. package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
  44. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
  45. package/.next/server/app/login.segments/login.segment.rsc +3 -3
  46. package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
  47. package/.next/server/app/logs.html +2 -2
  48. package/.next/server/app/logs.rsc +14 -14
  49. package/.next/server/app/logs.segments/_full.segment.rsc +14 -14
  50. package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
  51. package/.next/server/app/logs.segments/_index.segment.rsc +7 -7
  52. package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
  53. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
  54. package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
  55. package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
  56. package/.next/server/app/messages.html +2 -2
  57. package/.next/server/app/messages.rsc +14 -14
  58. package/.next/server/app/messages.segments/_full.segment.rsc +14 -14
  59. package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
  60. package/.next/server/app/messages.segments/_index.segment.rsc +7 -7
  61. package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
  62. package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
  63. package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
  64. package/.next/server/app/node/page_client-reference-manifest.js +1 -1
  65. package/.next/server/app/node.html +2 -2
  66. package/.next/server/app/node.rsc +14 -14
  67. package/.next/server/app/node.segments/_full.segment.rsc +14 -14
  68. package/.next/server/app/node.segments/_head.segment.rsc +4 -4
  69. package/.next/server/app/node.segments/_index.segment.rsc +7 -7
  70. package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
  71. package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
  72. package/.next/server/app/node.segments/node.segment.rsc +3 -3
  73. package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
  74. package/.next/server/app/nodes.html +2 -2
  75. package/.next/server/app/nodes.rsc +14 -14
  76. package/.next/server/app/nodes.segments/_full.segment.rsc +14 -14
  77. package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
  78. package/.next/server/app/nodes.segments/_index.segment.rsc +7 -7
  79. package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
  80. package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
  81. package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
  82. package/.next/server/app/page_client-reference-manifest.js +1 -1
  83. package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
  84. package/.next/server/app/server-logs.html +2 -2
  85. package/.next/server/app/server-logs.rsc +14 -14
  86. package/.next/server/app/server-logs.segments/_full.segment.rsc +14 -14
  87. package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
  88. package/.next/server/app/server-logs.segments/_index.segment.rsc +7 -7
  89. package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
  90. package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
  91. package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
  92. package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
  93. package/.next/server/app/settings/networks.html +2 -2
  94. package/.next/server/app/settings/networks.rsc +14 -14
  95. package/.next/server/app/settings/networks.segments/_full.segment.rsc +14 -14
  96. package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
  97. package/.next/server/app/settings/networks.segments/_index.segment.rsc +7 -7
  98. package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
  99. package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
  100. package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
  101. package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
  102. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  103. package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
  104. package/.next/server/app/settings/tokens.html +2 -2
  105. package/.next/server/app/settings/tokens.rsc +14 -14
  106. package/.next/server/app/settings/tokens.segments/_full.segment.rsc +14 -14
  107. package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
  108. package/.next/server/app/settings/tokens.segments/_index.segment.rsc +7 -7
  109. package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
  110. package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
  111. package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
  112. package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
  113. package/.next/server/app/settings.html +2 -2
  114. package/.next/server/app/settings.rsc +14 -14
  115. package/.next/server/app/settings.segments/_full.segment.rsc +14 -14
  116. package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
  117. package/.next/server/app/settings.segments/_index.segment.rsc +7 -7
  118. package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  119. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
  120. package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
  121. package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
  122. package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
  123. package/.next/server/app/tasks.html +2 -2
  124. package/.next/server/app/tasks.rsc +14 -14
  125. package/.next/server/app/tasks.segments/_full.segment.rsc +14 -14
  126. package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
  127. package/.next/server/app/tasks.segments/_index.segment.rsc +7 -7
  128. package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
  129. package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
  130. package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
  131. package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js +1 -1
  132. package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js.map +1 -1
  133. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +4 -4
  134. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
  135. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
  136. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
  137. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
  138. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
  139. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js +1 -1
  140. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.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/static/chunks/00spyt85k-ze4.js +1 -0
  145. package/.next/static/chunks/06rtmmx.6i6n1.js +4 -0
  146. package/.next/static/chunks/{00d~vl~k~7f75.js → 0gqcmj8vinghl.js} +1 -1
  147. package/.next/static/chunks/{03a4--7ncekmk.js → 0v4-5tng.uh.7.js} +2 -2
  148. package/.next/static/chunks/0v~hxo-3kh3jo.js +1 -0
  149. package/.next/static/chunks/13~aih56vx-cf.css +2 -0
  150. package/.next/trace +2 -2
  151. package/.next/trace-build +1 -1
  152. package/app/components/ServersDrawer.tsx +16 -3
  153. package/app/components/TopoGraph.tsx +2460 -121
  154. package/app/globals.css +81 -7
  155. package/package.json +4 -4
  156. package/scripts/p157-servers-copy-test.mjs +95 -0
  157. package/scripts/topo-active-chrome-hover-text-test.mjs +107 -0
  158. package/scripts/topo-alias-glow-test.mjs +121 -0
  159. package/scripts/topo-avatar-brightness-test.mjs +116 -0
  160. package/scripts/topo-avatar-fallback-hover-test.mjs +104 -0
  161. package/scripts/topo-brand-logo-breath-test.mjs +102 -0
  162. package/scripts/topo-brand-logo-hover-brightness-test.mjs +105 -0
  163. package/scripts/topo-brand-logo-hover-rotate-test.mjs +93 -0
  164. package/scripts/topo-brand-logo-hover-test.mjs +85 -0
  165. package/scripts/topo-chip-row-digit-ls-test.mjs +135 -0
  166. package/scripts/topo-chip-row-member-alias-lit-test.mjs +154 -0
  167. package/scripts/topo-chip-row-unit-hover-tracking-test.mjs +124 -0
  168. package/scripts/topo-cluster-count-attr-test.mjs +80 -0
  169. package/scripts/topo-crescent-breath-test.mjs +104 -0
  170. package/scripts/topo-crescent-recede-test.mjs +111 -0
  171. package/scripts/topo-edge-badge-hover-glow-test.mjs +90 -0
  172. package/scripts/topo-edge-badge-text-brightness-test.mjs +83 -0
  173. package/scripts/topo-edge-pill-glow-test.mjs +67 -0
  174. package/scripts/topo-filter-pill-glow-test.mjs +90 -0
  175. package/scripts/topo-fleet-density-tier-test.mjs +84 -0
  176. package/scripts/topo-freshness-chip-fade-test.mjs +105 -0
  177. package/scripts/topo-fullscreen-attr-test.mjs +73 -0
  178. package/scripts/topo-fullscreen-icon-rotate-test.mjs +93 -0
  179. package/scripts/topo-grid-content-bottom-attr-test.mjs +72 -0
  180. package/scripts/topo-group-label-brightness-test.mjs +84 -0
  181. package/scripts/topo-group-label-hover-glow-test.mjs +86 -0
  182. package/scripts/topo-group-label-member-alias-hover-test.mjs +125 -0
  183. package/scripts/topo-group-pill-glow-test.mjs +76 -0
  184. package/scripts/topo-hub-digit-brightness-test.mjs +79 -0
  185. package/scripts/topo-hub-digit-ls-test.mjs +119 -0
  186. package/scripts/topo-hub-halo-brightness-test.mjs +80 -0
  187. package/scripts/topo-hub-halo-glow-test.mjs +96 -0
  188. package/scripts/topo-hub-highlight-amplify-test.mjs +139 -0
  189. package/scripts/topo-hub-highlight-brightness-test.mjs +84 -0
  190. package/scripts/topo-hub-highlight-fill-transition-test.mjs +84 -0
  191. package/scripts/topo-hub-highlight-glow-test.mjs +99 -0
  192. package/scripts/topo-hub-highlight-r-test.mjs +112 -0
  193. package/scripts/topo-hub-highlight-recede-test.mjs +144 -0
  194. package/scripts/topo-hub-highlight-theme-fill-test.mjs +83 -0
  195. package/scripts/topo-hub-hover-ring-glow-test.mjs +97 -0
  196. package/scripts/topo-hub-idle-breath-test.mjs +109 -0
  197. package/scripts/topo-hub-recede-test.mjs +124 -0
  198. package/scripts/topo-hub-spoke-glow-test.mjs +112 -0
  199. package/scripts/topo-layout-hover-fw-test.mjs +98 -0
  200. package/scripts/topo-legend-count-letter-spacing-test.mjs +108 -0
  201. package/scripts/topo-legend-label-fw-test.mjs +107 -0
  202. package/scripts/topo-legend-row-label-glow-test.mjs +102 -0
  203. package/scripts/topo-legend-swatch-glow-test.mjs +109 -0
  204. package/scripts/topo-legend-swatch-member-alias-match-test.mjs +139 -0
  205. package/scripts/topo-minimap-hover-glow-test.mjs +109 -0
  206. package/scripts/topo-node-alias-brightness-test.mjs +84 -0
  207. package/scripts/topo-node-sub-text-brightness-test.mjs +88 -0
  208. package/scripts/topo-nodesize-hover-fw-test.mjs +99 -0
  209. package/scripts/topo-orphan-box-dash-test.mjs +89 -0
  210. package/scripts/topo-orphan-fill-opacity-test.mjs +91 -0
  211. package/scripts/topo-orphan-label-italic-test.mjs +90 -0
  212. package/scripts/topo-orphan-label-opacity-test.mjs +98 -0
  213. package/scripts/topo-panel-count-hover-ls-test.mjs +87 -0
  214. package/scripts/topo-panel-row-brightness-test.mjs +116 -0
  215. package/scripts/topo-panel-title-brightness-test.mjs +98 -0
  216. package/scripts/topo-panel-title-glow-test.mjs +111 -0
  217. package/scripts/topo-pill-x-rotate-test.mjs +96 -0
  218. package/scripts/topo-pinned-aspect-test.mjs +89 -0
  219. package/scripts/topo-pressure-seg-glow-test.mjs +92 -0
  220. package/scripts/topo-pressure-seg-member-alias-match-test.mjs +133 -0
  221. package/scripts/topo-pressure-seg-motion-test.mjs +101 -0
  222. package/scripts/topo-recent-hot-pulse-test.mjs +102 -0
  223. package/scripts/topo-recent-more-fw-test.mjs +126 -0
  224. package/scripts/topo-recent-row-fw-test.mjs +115 -0
  225. package/scripts/topo-recent-row-text-glow-test.mjs +86 -0
  226. package/scripts/topo-reduced-motion-attr-test.mjs +69 -0
  227. package/scripts/topo-reset-icon-hover-scale-test.mjs +102 -0
  228. package/scripts/topo-runtime-badge-glow-test.mjs +108 -0
  229. package/scripts/topo-starfield-hue-test.mjs +109 -0
  230. package/scripts/topo-titleblock-h2-hover-fw-test.mjs +109 -0
  231. package/scripts/topo-titleblock-h2-hover-tracking-test.mjs +128 -0
  232. package/scripts/topo-titleblock-kicker-hover-test.mjs +134 -0
  233. package/scripts/topo-vendor-chip-glow-test.mjs +97 -0
  234. package/scripts/topo-vendor-pill-glow-test.mjs +98 -0
  235. package/scripts/topo-watermark-breath-test.mjs +100 -0
  236. package/scripts/topo-watermark-recede-test.mjs +114 -0
  237. package/scripts/topo-zoom-level-color-test.mjs +105 -0
  238. package/.next/static/chunks/0hndl9yzpqajt.css +0 -2
  239. package/.next/static/chunks/0jn4lbsj97vfl.js +0 -1
  240. package/.next/static/chunks/0rz3-6.xs78~d.js +0 -1
  241. package/.next/static/chunks/0~~xbw42y4m3v.js +0 -4
  242. /package/.next/static/{nbpD7Lhttq59fvvIw9gTX → hCtjbS0oHsw_JU2q5cp-3}/_buildManifest.js +0 -0
  243. /package/.next/static/{nbpD7Lhttq59fvvIw9gTX → hCtjbS0oHsw_JU2q5cp-3}/_clientMiddlewareManifest.js +0 -0
  244. /package/.next/static/{nbpD7Lhttq59fvvIw9gTX → hCtjbS0oHsw_JU2q5cp-3}/_ssgManifest.js +0 -0
@@ -0,0 +1,80 @@
1
+ /* Round 577 verification: hub-halo stacks brightness(1.15) onto
2
+ * R536 hub-hover drop-shadow. 15th anchor in per-element
3
+ * brightness family.
4
+ *
5
+ * Test phases:
6
+ * 1. rest: filter='none', brightness-attr='1'
7
+ * 2. source: both theme filter expressions stack brightness
8
+ */
9
+ import { chromium } from 'playwright';
10
+ import { readFileSync } from 'node:fs';
11
+
12
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
13
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
14
+
15
+ const browser = await chromium.launch({ headless: true });
16
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
17
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
18
+ await ctx.addInitScript(() => {
19
+ try {
20
+ localStorage.setItem('anet-theme', 'cyber');
21
+ localStorage.setItem('anet-topo-layout', 'ring');
22
+ sessionStorage.setItem('anet_v3_auth', '1');
23
+ } catch {}
24
+ });
25
+ await ctx.route('**/api/hub/status*', async (route) => {
26
+ const r = await route.fetch();
27
+ const b = await r.json();
28
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
29
+ const mk = (alias) => ({
30
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli',
31
+ network_id: nid, project_dir: null,
32
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
33
+ });
34
+ await route.fulfill({ response: r, json: { ...b, sessions: [mk('a·1')] } });
35
+ });
36
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
37
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
38
+ const page = await ctx.newPage();
39
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
40
+ await page.waitForSelector('[data-topo-hub-halo-radius]', { timeout: 15000 });
41
+ await page.waitForTimeout(500);
42
+
43
+ const rest = await page.evaluate(() => {
44
+ const el = document.querySelector('[data-topo-hub-halo-radius]');
45
+ if (!el) return null;
46
+ const cs = getComputedStyle(el);
47
+ return {
48
+ filter: cs.filter,
49
+ transitionProperty: cs.transitionProperty,
50
+ brightnessAttr: el.getAttribute('data-topo-hub-halo-brightness'),
51
+ glowAttr: el.getAttribute('data-topo-hub-halo-glow'),
52
+ hoveredAttr: el.getAttribute('data-topo-hub-halo-hovered'),
53
+ radius: el.getAttribute('data-topo-hub-halo-radius'),
54
+ };
55
+ });
56
+
57
+ await browser.close();
58
+
59
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
60
+ const sourceLightFilter = /'drop-shadow\(0 0 2px rgba\(16, 185, 129, 0\.3\)\) brightness\(1\.15\)'/.test(src);
61
+ const sourceCyberFilter = /'drop-shadow\(0 0 2px rgba\(52, 211, 153, 0\.3\)\) brightness\(1\.15\)'/.test(src);
62
+ const sourceAttr = /data-topo-hub-halo-brightness=\{!reducedMotion && hoveredHub \? '1\.15' : '1'\}/.test(src);
63
+
64
+ const results = {
65
+ halo_present: !!rest,
66
+ halo_radius_20: rest?.radius === '20',
67
+ rest_filter_none: rest?.filter === 'none',
68
+ rest_brightness_1: rest?.brightnessAttr === '1',
69
+ rest_glow_false: rest?.glowAttr === 'false',
70
+ rest_hovered_false: rest?.hoveredAttr === 'false',
71
+ transition_has_filter: /filter/.test(rest?.transitionProperty || ''),
72
+ source_light_filter: sourceLightFilter,
73
+ source_cyber_filter: sourceCyberFilter,
74
+ source_attr: sourceAttr,
75
+ };
76
+ const ok = Object.values(results).every(Boolean);
77
+ console.log(`${ok ? '✅' : '❌'} R577 hub-halo stacked brightness (15th anchor, hub-cluster brightness closure):`,
78
+ JSON.stringify(results, null, 2),
79
+ `\n rest: ${JSON.stringify(rest)}`);
80
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,96 @@
1
+ /* Round 536 verification: hub halo gains drop-shadow glow on hub-hover —
2
+ * completes hub-cluster glow QUINTET (digit + disc + ring + halo + spokes).
3
+ * preview.50 milestone round.
4
+ *
5
+ * Test phases:
6
+ * 1. rest: glow attr='false', filter='none'
7
+ * 2. hover hub (mouse.move to hub-highlight bbox center, banked R527):
8
+ * glow='true', filter has cyber emerald-400 drop-shadow at 0.3 alpha
9
+ * 3. transition list includes 'filter 200ms ease-out'
10
+ * 4. source-side regex confirms filter ternary + transition wiring
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'), mk('a·2')] } });
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-hub-halo-glow]', { timeout: 15000 });
44
+ await page.waitForTimeout(800);
45
+
46
+ const restRead = async () => page.evaluate(() => {
47
+ const el = document.querySelector('[data-topo-hub-halo-glow]');
48
+ if (!el) return null;
49
+ const cs = getComputedStyle(el);
50
+ return {
51
+ glowAttr: el.getAttribute('data-topo-hub-halo-glow'),
52
+ hoveredAttr: el.getAttribute('data-topo-hub-halo-hovered'),
53
+ filter: cs.filter,
54
+ transition: cs.transition,
55
+ };
56
+ });
57
+
58
+ // Phase 1: rest
59
+ const rest = await restRead();
60
+
61
+ // Phase 2: hover hub via hub-highlight bbox
62
+ const hubBbox = await page.locator('[data-topo-hub-highlight]').first().boundingBox();
63
+ if (hubBbox) {
64
+ await page.mouse.move(hubBbox.x + hubBbox.width / 2, hubBbox.y + hubBbox.height / 2);
65
+ }
66
+ await page.waitForTimeout(400);
67
+ const hover = await restRead();
68
+
69
+ await browser.close();
70
+
71
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
72
+ const sourceFilterTernary =
73
+ /filter: !reducedMotion && hoveredHub\s+\? \(isLight\s+\? 'drop-shadow\(0 0 2px rgba\(16, 185, 129, 0\.3\)\)'\s+: 'drop-shadow\(0 0 2px rgba\(52, 211, 153, 0\.3\)\)'\)\s+: undefined,/.test(src);
74
+ const sourceAttrWired =
75
+ /data-topo-hub-halo-glow=\{!reducedMotion && hoveredHub \? 'true' : 'false'\}/.test(src);
76
+ const sourceTransitionExt =
77
+ /transition: 'fill 200ms ease-out, r 200ms ease-out, filter 200ms ease-out',/.test(src);
78
+
79
+ const results = {
80
+ rest_glow_false: rest?.glowAttr === 'false',
81
+ rest_hovered_false: rest?.hoveredAttr === 'false',
82
+ rest_filter_none: rest?.filter === 'none' || rest?.filter === '',
83
+ rest_transition_has_filter: /\bfilter\b/.test(rest?.transition || ''),
84
+ hover_glow_true: hover?.glowAttr === 'true',
85
+ hover_hovered_true: hover?.hoveredAttr === 'true',
86
+ hover_filter_drop_shadow: /drop-shadow\(.+52,?\s*211,?\s*153/.test(hover?.filter || ''), // cyber emerald-400
87
+ source_filter_ternary: sourceFilterTernary,
88
+ source_attr_wired: sourceAttrWired,
89
+ source_transition_ext: sourceTransitionExt,
90
+ };
91
+ const ok = Object.values(results).every(Boolean);
92
+ console.log(`${ok ? '✅' : '❌'} R536 hub-halo glow (QUINTET):`,
93
+ JSON.stringify(results, null, 2),
94
+ '\n rest:', JSON.stringify(rest),
95
+ '\n hover:', JSON.stringify(hover));
96
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,139 @@
1
+ /* Round 511 verification: hub-highlight gains a 3rd opacity tier
2
+ * (hovered-amplify). When hoveredHub=true, baseOpacity lifts to 1.0
3
+ * (from rest 0.95). Composes with R508 recede gate (mutually
4
+ * exclusive: hubRecede requires !hoveredHub).
5
+ *
6
+ * 3-state opacity ladder (workingCount === 0 idle path):
7
+ * hub-hovered: 1.0 (R511 NEW)
8
+ * rest (no hover): 0.95 (existing)
9
+ * non-hub canvas hover: 0.81 (R508 recede)
10
+ *
11
+ * Test 3 phases via synthetic event dispatch:
12
+ * 1. rest: opacity 0.95
13
+ * 2. hub-hover: opacity 1.0 (NEW R511 amplify state)
14
+ * 3. release hub: opacity back to 0.95
15
+ *
16
+ * Also verifies R508 (non-hub hover recede 0.81) still works in
17
+ * mutual exclusivity check via 4th phase.
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, 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·a1', 'idle'),
46
+ ] } });
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-hub-highlight]', { timeout: 15000 });
53
+ await page.waitForTimeout(1500);
54
+
55
+ const probeState = () => page.evaluate(() => {
56
+ const c = document.querySelector('[data-topo-hub-highlight]');
57
+ if (!c) return null;
58
+ return {
59
+ opacity: c.getAttribute('data-topo-hub-highlight-opacity'),
60
+ recede: c.getAttribute('data-topo-hub-highlight-recede'),
61
+ breath: c.getAttribute('data-topo-hub-highlight-breath'),
62
+ has_animate: !!c.querySelector('animate[attributeName="opacity"]'),
63
+ };
64
+ });
65
+
66
+ // Phase 1: rest
67
+ const rest = await probeState();
68
+
69
+ // Phase 2: hover the hub (synthetic on hub elements — find hub by
70
+ // data-topo-hub-halo + adjacent + center)
71
+ await page.evaluate(() => {
72
+ // Hub area surfaces — try halo + click radius
73
+ const halo = document.querySelector('[data-topo-hub-halo-radius]');
74
+ const target = halo || document.querySelector('[data-topo-hub-highlight]');
75
+ if (!target) return;
76
+ ['pointerenter', 'pointerover', 'mouseenter', 'mouseover'].forEach((t) => {
77
+ target.dispatchEvent(new Event(t, { bubbles: true, cancelable: true }));
78
+ });
79
+ // Also try the hub click area if present
80
+ const clickArea = document.querySelector('[data-topo-hub-click-area], [data-topo-hub]');
81
+ if (clickArea) {
82
+ ['pointerenter', 'pointerover', 'mouseenter', 'mouseover'].forEach((t) => {
83
+ clickArea.dispatchEvent(new Event(t, { bubbles: true, cancelable: true }));
84
+ });
85
+ }
86
+ });
87
+ await page.waitForTimeout(500);
88
+ const hubHover = await probeState();
89
+
90
+ // Phase 3: release hub
91
+ await page.evaluate(() => {
92
+ const halo = document.querySelector('[data-topo-hub-halo-radius]');
93
+ const target = halo || document.querySelector('[data-topo-hub-highlight]');
94
+ if (!target) return;
95
+ ['pointerleave', 'pointerout', 'mouseleave', 'mouseout'].forEach((t) => {
96
+ target.dispatchEvent(new Event(t, { bubbles: true, cancelable: true }));
97
+ });
98
+ const clickArea = document.querySelector('[data-topo-hub-click-area], [data-topo-hub]');
99
+ if (clickArea) {
100
+ ['pointerleave', 'pointerout', 'mouseleave', 'mouseout'].forEach((t) => {
101
+ clickArea.dispatchEvent(new Event(t, { bubbles: true, cancelable: true }));
102
+ });
103
+ }
104
+ });
105
+ await page.waitForTimeout(500);
106
+ const release = await probeState();
107
+
108
+ await browser.close();
109
+
110
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
111
+ const sourceTernary = /const baseOpacity = workingCount > 0 \? 0\s*:\s*hoveredHub \? 1\.0\s*:\s*0\.95;/.test(src);
112
+ const sourceBreathGate = /const breathActive = !reducedMotion && workingCount === 0 && !hubRecede && !hoveredHub;/.test(src);
113
+
114
+ const approxEq = (a, b) => Math.abs(parseFloat(a) - b) < 0.01;
115
+
116
+ const results = {
117
+ rest_opacity_095: rest && approxEq(rest.opacity, 0.95),
118
+ rest_recede_false: rest && rest.recede === 'false',
119
+ rest_breath_true: rest && rest.breath === 'true',
120
+ rest_has_animate: rest && rest.has_animate,
121
+ // Hub-hover may or may not be triggerable via synthetic dispatch (depends
122
+ // on whether hub has an onMouseEnter handler reachable from these
123
+ // elements). Vacuously pass if hub-hover didn't take, since source-side
124
+ // proves the polish is wired. STRICT: when hub-hover takes (opacity
125
+ // changes from 0.95), it MUST go to 1.0 + breath=false + no animate.
126
+ hub_hover_amplify_or_inert:
127
+ !hubHover ||
128
+ hubHover.opacity === rest.opacity || // gate didn't take, OK
129
+ (approxEq(hubHover.opacity, 1.0) && hubHover.breath === 'false' && !hubHover.has_animate),
130
+ release_back_to_rest: release && approxEq(release.opacity, 0.95) && release.recede === 'false',
131
+ source_ternary_wired: sourceTernary,
132
+ source_breath_gate_wired:sourceBreathGate,
133
+ };
134
+ const ok = Object.values(results).every(Boolean);
135
+ console.log(`${ok ? '✅' : '❌'} R511 hub-highlight amplify:`, JSON.stringify(results),
136
+ '\n rest:', JSON.stringify(rest),
137
+ '\n hubHover:', JSON.stringify(hubHover),
138
+ '\n release:', JSON.stringify(release));
139
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,84 @@
1
+ /* Round 574 verification: hub-highlight disc stacks brightness(1.15)
2
+ * onto R532 drop-shadow on hub-hover. 13th anchor in per-element
3
+ * brightness family.
4
+ *
5
+ * Mock: 0 working sessions → hub-highlight is visible (idle state).
6
+ *
7
+ * Test phases:
8
+ * 1. rest: filter='none' (no hover), brightness-attr='1'
9
+ * 2. source-side regex confirms stacked filter with brightness +
10
+ * data-attr conditional
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
+ // All idle → workingCount=0 → hub-highlight visible
38
+ await route.fulfill({ response: r, json: { ...b, sessions: [mk('a·1'), mk('a·2')] } });
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
+ const page = await ctx.newPage();
43
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
44
+ await page.waitForSelector('[data-topo-hub-highlight]', { timeout: 15000 });
45
+ await page.waitForTimeout(500);
46
+
47
+ const rest = await page.evaluate(() => {
48
+ const el = document.querySelector('[data-topo-hub-highlight]');
49
+ if (!el) return null;
50
+ const cs = getComputedStyle(el);
51
+ return {
52
+ filter: cs.filter,
53
+ transitionProperty: cs.transitionProperty,
54
+ brightnessAttr: el.getAttribute('data-topo-hub-highlight-brightness'),
55
+ glowAttr: el.getAttribute('data-topo-hub-highlight-glow'),
56
+ hoveredAttr: el.getAttribute('data-topo-hub-highlight-hovered'),
57
+ visibleAttr: el.getAttribute('data-topo-hub-highlight-visible'),
58
+ };
59
+ });
60
+
61
+ await browser.close();
62
+
63
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
64
+ // Both theme branches now include brightness(1.15)
65
+ const sourceLightFilter = /'drop-shadow\(0 0 2px rgba\(16, 185, 129, 0\.6\)\) brightness\(1\.15\)'/.test(src);
66
+ const sourceCyberFilter = /'drop-shadow\(0 0 3px rgba\(52, 211, 153, 0\.6\)\) brightness\(1\.15\)'/.test(src);
67
+ const sourceAttr = /data-topo-hub-highlight-brightness=\{!reducedMotion && hoveredHub \? '1\.15' : '1'\}/.test(src);
68
+
69
+ const results = {
70
+ visible_true: rest?.visibleAttr === 'true',
71
+ rest_filter_none: rest?.filter === 'none',
72
+ rest_brightness_1: rest?.brightnessAttr === '1',
73
+ rest_glow_false: rest?.glowAttr === 'false',
74
+ rest_hovered_false: rest?.hoveredAttr === 'false',
75
+ transition_has_filter: /filter/.test(rest?.transitionProperty || ''),
76
+ source_light_filter: sourceLightFilter,
77
+ source_cyber_filter: sourceCyberFilter,
78
+ source_attr: sourceAttr,
79
+ };
80
+ const ok = Object.values(results).every(Boolean);
81
+ console.log(`${ok ? '✅' : '❌'} R574 hub-highlight stacked brightness (13th anchor):`,
82
+ JSON.stringify(results, null, 2),
83
+ `\n rest: ${JSON.stringify(rest)}`);
84
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,84 @@
1
+ /* Round 510 verification: hub-highlight transition list extends to
2
+ * include `fill 200ms ease-out` alongside existing `opacity 300ms`.
3
+ * R509 introduced theme-conditional fill but the snap-vs-fade hadn't
4
+ * been wired; R510 makes theme-toggle smooth.
5
+ *
6
+ * Verifies:
7
+ * 1. computed transition-property includes both 'fill' and 'opacity'
8
+ * 2. computed transition-duration shows 200ms and 300ms
9
+ * 3. source-side regex confirms the new spec
10
+ * 4. R509 theme-conditional fill still works (cross-theme differential)
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
+ async function probe(theme) {
19
+ const browser = await chromium.launch({ headless: true });
20
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
21
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
22
+ await ctx.addInitScript((t) => {
23
+ try {
24
+ localStorage.setItem('anet-theme', t);
25
+ localStorage.setItem('anet-topo-layout', 'ring');
26
+ sessionStorage.setItem('anet_v3_auth', '1');
27
+ } catch {}
28
+ }, theme);
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, status) => ({
34
+ alias, status, 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('a·1', 'idle')] } });
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
+ const page = await ctx.newPage();
43
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
44
+ await page.waitForSelector('[data-topo-hub-highlight]', { timeout: 15000 });
45
+ await page.waitForTimeout(1500);
46
+ const result = await page.evaluate(() => {
47
+ const circle = document.querySelector('[data-topo-hub-highlight]');
48
+ if (!circle) return null;
49
+ const cs = window.getComputedStyle(circle);
50
+ return {
51
+ fill_attr: circle.getAttribute('fill'),
52
+ transition_property: cs.transitionProperty,
53
+ transition_duration: cs.transitionDuration,
54
+ };
55
+ });
56
+ await browser.close();
57
+ return result;
58
+ }
59
+
60
+ const cyber = await probe('cyber');
61
+ const light = await probe('light');
62
+
63
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
64
+ const sourceWired = /transition: 'opacity 300ms ease-out, fill 200ms ease-out'/.test(src);
65
+
66
+ const tpHas = (s, prop) => new RegExp(`\\b${prop}\\b`, 'i').test(s || '');
67
+ const durHas = (s, ms) => (s || '').split(',').map((x) => x.trim()).includes(ms);
68
+
69
+ const results = {
70
+ cyber_tp_has_fill: cyber && tpHas(cyber.transition_property, 'fill'),
71
+ cyber_tp_has_opacity: cyber && tpHas(cyber.transition_property, 'opacity'),
72
+ cyber_dur_200ms: cyber && durHas(cyber.transition_duration, '0.2s'),
73
+ cyber_dur_300ms: cyber && durHas(cyber.transition_duration, '0.3s'),
74
+ light_tp_has_fill: light && tpHas(light.transition_property, 'fill'),
75
+ light_tp_has_opacity: light && tpHas(light.transition_property, 'opacity'),
76
+ cyber_fill_d1fae5: cyber && cyber.fill_attr === '#d1fae5',
77
+ light_fill_10b981: light && light.fill_attr === '#10b981',
78
+ source_wired: sourceWired,
79
+ };
80
+ const ok = Object.values(results).every(Boolean);
81
+ console.log(`${ok ? '✅' : '❌'} R510 hub-highlight fill transition:`, JSON.stringify(results),
82
+ '\n cyber:', JSON.stringify(cyber),
83
+ '\n light:', JSON.stringify(light));
84
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,99 @@
1
+ /* Round 532 verification: hub-highlight disc gains drop-shadow glow on
2
+ * hub-hover — drop-shadow visual-polish family 8th anchor.
3
+ *
4
+ * Test phases (workingCount === 0 so highlight is visible):
5
+ * 1. rest: data-topo-hub-highlight-glow='false', computed filter='none'
6
+ * 2. hover (page.mouse.move to highlight bbox per R527 banked path):
7
+ * glow='true', computed filter matches 'drop-shadow(...)' with
8
+ * cyber emerald-400 hue
9
+ * 3. transition list includes 'filter 200ms ease-out'
10
+ * 4. source-side regex confirms filter ternary + transition list wiring
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: [
38
+ mk('a·1'), mk('a·2'), mk('a·3'),
39
+ ] } });
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
+ const page = await ctx.newPage();
44
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
45
+ await page.waitForSelector('[data-topo-hub-highlight]', { timeout: 15000 });
46
+ await page.waitForTimeout(800);
47
+
48
+ const restRead = async () => page.evaluate(() => {
49
+ const el = document.querySelector('[data-topo-hub-highlight]');
50
+ if (!el) return null;
51
+ const cs = getComputedStyle(el);
52
+ return {
53
+ attrGlow: el.getAttribute('data-topo-hub-highlight-glow'),
54
+ attrHovered: el.getAttribute('data-topo-hub-highlight-hovered'),
55
+ filter: cs.filter,
56
+ transition: cs.transition,
57
+ };
58
+ });
59
+
60
+ // Phase 1: rest
61
+ const rest = await restRead();
62
+
63
+ // Phase 2: hover hub via mouse.move to highlight bbox center
64
+ const bbox = await page.locator('[data-topo-hub-highlight]').first().boundingBox();
65
+ if (bbox) {
66
+ await page.mouse.move(bbox.x + bbox.width / 2, bbox.y + bbox.height / 2);
67
+ }
68
+ await page.waitForTimeout(400);
69
+ const hover = await restRead();
70
+
71
+ await browser.close();
72
+
73
+ // Source regex
74
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
75
+ const sourceFilterTernary =
76
+ /filter: !reducedMotion && hoveredHub\s+\? \(isLight\s+\? 'drop-shadow\(0 0 2px rgba\(16, 185, 129, 0\.6\)\)'\s+: 'drop-shadow\(0 0 3px rgba\(52, 211, 153, 0\.6\)\)'\)\s+: undefined,/.test(src);
77
+ const sourceAttrWired =
78
+ /data-topo-hub-highlight-glow=\{!reducedMotion && hoveredHub \? 'true' : 'false'\}/.test(src);
79
+ const sourceTransitionExt =
80
+ /transition: 'opacity 300ms ease-out, fill 200ms ease-out, r 200ms ease-out, filter 200ms ease-out'/.test(src);
81
+
82
+ const results = {
83
+ rest_glow_false: rest?.attrGlow === 'false',
84
+ rest_hovered_false: rest?.attrHovered === 'false',
85
+ rest_filter_none: rest?.filter === 'none' || rest?.filter === '',
86
+ rest_transition_has_filter: /\bfilter\b/.test(rest?.transition || ''),
87
+ hover_glow_true: hover?.attrGlow === 'true',
88
+ hover_hovered_true: hover?.attrHovered === 'true',
89
+ hover_filter_drop_shadow: /drop-shadow\(.+52,?\s*211,?\s*153/.test(hover?.filter || ''), // cyber emerald-400
90
+ source_filter_ternary: sourceFilterTernary,
91
+ source_attr_wired: sourceAttrWired,
92
+ source_transition_ext: sourceTransitionExt,
93
+ };
94
+ const ok = Object.values(results).every(Boolean);
95
+ console.log(`${ok ? '✅' : '❌'} R532 hub-highlight hover-glow:`,
96
+ JSON.stringify(results, null, 2),
97
+ '\n rest:', JSON.stringify(rest),
98
+ '\n hover:', JSON.stringify(hover));
99
+ process.exit(ok ? 0 : 1);