@sleep2agi/agent-network-dashboard 0.5.3-preview.1 → 0.5.3-preview.100

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 (261) 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/0-c2b_r~s.a00.js +1 -0
  145. package/.next/static/chunks/{0ce~u5hn~wege.js → 0nvxj45vzw-1t.js} +1 -1
  146. package/.next/static/chunks/{03a4--7ncekmk.js → 0v4-5tng.uh.7.js} +2 -2
  147. package/.next/static/chunks/0v9i~z.k.2uji.js +1 -0
  148. package/.next/static/chunks/0w3dd-f9bl6xs.js +4 -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 +2747 -140
  154. package/app/globals.css +142 -6
  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-press-test.mjs +93 -0
  168. package/scripts/topo-chip-row-tier-glow-brightness-test.mjs +99 -0
  169. package/scripts/topo-chip-row-unit-hover-tracking-test.mjs +124 -0
  170. package/scripts/topo-chrome-press-fullstrip-test.mjs +105 -0
  171. package/scripts/topo-chrome-press-scale-test.mjs +100 -0
  172. package/scripts/topo-cluster-count-attr-test.mjs +80 -0
  173. package/scripts/topo-crescent-breath-test.mjs +104 -0
  174. package/scripts/topo-crescent-recede-test.mjs +111 -0
  175. package/scripts/topo-edge-badge-hover-glow-test.mjs +90 -0
  176. package/scripts/topo-edge-badge-text-brightness-test.mjs +83 -0
  177. package/scripts/topo-edge-particle-brightness-test.mjs +82 -0
  178. package/scripts/topo-edge-pill-glow-test.mjs +67 -0
  179. package/scripts/topo-edge-visible-brightness-test.mjs +84 -0
  180. package/scripts/topo-endpoint-ring-brightness-test.mjs +83 -0
  181. package/scripts/topo-filter-pill-glow-test.mjs +90 -0
  182. package/scripts/topo-filter-pills-press-test.mjs +96 -0
  183. package/scripts/topo-fleet-density-tier-test.mjs +84 -0
  184. package/scripts/topo-flow-rail-brightness-test.mjs +80 -0
  185. package/scripts/topo-focus-outline-transition-test.mjs +107 -0
  186. package/scripts/topo-freshness-chip-fade-test.mjs +105 -0
  187. package/scripts/topo-fullscreen-attr-test.mjs +73 -0
  188. package/scripts/topo-fullscreen-icon-rotate-test.mjs +93 -0
  189. package/scripts/topo-grid-content-bottom-attr-test.mjs +72 -0
  190. package/scripts/topo-group-label-brightness-test.mjs +84 -0
  191. package/scripts/topo-group-label-hover-glow-test.mjs +86 -0
  192. package/scripts/topo-group-label-member-alias-hover-test.mjs +125 -0
  193. package/scripts/topo-group-pill-glow-test.mjs +76 -0
  194. package/scripts/topo-hover-ring-duration-test.mjs +87 -0
  195. package/scripts/topo-hub-digit-brightness-test.mjs +79 -0
  196. package/scripts/topo-hub-digit-ls-test.mjs +119 -0
  197. package/scripts/topo-hub-halo-brightness-test.mjs +80 -0
  198. package/scripts/topo-hub-halo-glow-test.mjs +96 -0
  199. package/scripts/topo-hub-highlight-amplify-test.mjs +139 -0
  200. package/scripts/topo-hub-highlight-brightness-test.mjs +84 -0
  201. package/scripts/topo-hub-highlight-fill-transition-test.mjs +84 -0
  202. package/scripts/topo-hub-highlight-glow-test.mjs +99 -0
  203. package/scripts/topo-hub-highlight-r-test.mjs +112 -0
  204. package/scripts/topo-hub-highlight-recede-test.mjs +144 -0
  205. package/scripts/topo-hub-highlight-theme-fill-test.mjs +83 -0
  206. package/scripts/topo-hub-hover-ring-brightness-test.mjs +79 -0
  207. package/scripts/topo-hub-hover-ring-glow-test.mjs +97 -0
  208. package/scripts/topo-hub-idle-breath-test.mjs +109 -0
  209. package/scripts/topo-hub-recede-test.mjs +124 -0
  210. package/scripts/topo-hub-spoke-brightness-test.mjs +77 -0
  211. package/scripts/topo-hub-spoke-glow-test.mjs +112 -0
  212. package/scripts/topo-layout-hover-fw-test.mjs +98 -0
  213. package/scripts/topo-legend-count-letter-spacing-test.mjs +108 -0
  214. package/scripts/topo-legend-label-fw-test.mjs +107 -0
  215. package/scripts/topo-legend-row-label-glow-test.mjs +102 -0
  216. package/scripts/topo-legend-swatch-glow-test.mjs +109 -0
  217. package/scripts/topo-legend-swatch-member-alias-match-test.mjs +139 -0
  218. package/scripts/topo-minimap-hover-glow-test.mjs +109 -0
  219. package/scripts/topo-node-alias-brightness-test.mjs +84 -0
  220. package/scripts/topo-node-sub-text-brightness-test.mjs +88 -0
  221. package/scripts/topo-nodesize-hover-fw-test.mjs +99 -0
  222. package/scripts/topo-orphan-box-dash-test.mjs +89 -0
  223. package/scripts/topo-orphan-fill-opacity-test.mjs +91 -0
  224. package/scripts/topo-orphan-label-italic-test.mjs +90 -0
  225. package/scripts/topo-orphan-label-opacity-test.mjs +98 -0
  226. package/scripts/topo-panel-count-hover-ls-test.mjs +87 -0
  227. package/scripts/topo-panel-row-brightness-test.mjs +116 -0
  228. package/scripts/topo-panel-title-brightness-test.mjs +98 -0
  229. package/scripts/topo-panel-title-glow-test.mjs +111 -0
  230. package/scripts/topo-pill-x-rotate-test.mjs +96 -0
  231. package/scripts/topo-pinned-aspect-test.mjs +89 -0
  232. package/scripts/topo-pressure-seg-glow-test.mjs +92 -0
  233. package/scripts/topo-pressure-seg-member-alias-match-test.mjs +133 -0
  234. package/scripts/topo-pressure-seg-motion-test.mjs +101 -0
  235. package/scripts/topo-recent-hot-pulse-test.mjs +102 -0
  236. package/scripts/topo-recent-more-fw-test.mjs +126 -0
  237. package/scripts/topo-recent-row-fw-test.mjs +115 -0
  238. package/scripts/topo-recent-row-text-glow-test.mjs +86 -0
  239. package/scripts/topo-reduced-motion-attr-test.mjs +69 -0
  240. package/scripts/topo-reset-icon-hover-scale-test.mjs +102 -0
  241. package/scripts/topo-runtime-badge-brightness-test.mjs +78 -0
  242. package/scripts/topo-runtime-badge-glow-test.mjs +108 -0
  243. package/scripts/topo-starfield-hue-test.mjs +109 -0
  244. package/scripts/topo-status-ring-brightness-test.mjs +84 -0
  245. package/scripts/topo-svg-focus-transition-test.mjs +105 -0
  246. package/scripts/topo-titleblock-h2-hover-fw-test.mjs +109 -0
  247. package/scripts/topo-titleblock-h2-hover-tracking-test.mjs +128 -0
  248. package/scripts/topo-titleblock-kicker-hover-test.mjs +134 -0
  249. package/scripts/topo-vendor-activelinks-press-test.mjs +100 -0
  250. package/scripts/topo-vendor-chip-glow-test.mjs +97 -0
  251. package/scripts/topo-vendor-pill-glow-test.mjs +98 -0
  252. package/scripts/topo-watermark-breath-test.mjs +100 -0
  253. package/scripts/topo-watermark-recede-test.mjs +114 -0
  254. package/scripts/topo-zoom-level-color-test.mjs +105 -0
  255. package/.next/static/chunks/0a4hmfvj-81x5.css +0 -2
  256. package/.next/static/chunks/14m8prv3qgm45.js +0 -4
  257. package/.next/static/chunks/17fq.aa.hsdd..js +0 -1
  258. package/.next/static/chunks/17~las5t-t.kj.js +0 -1
  259. /package/.next/static/{cBZM18sgaGtUo-xccD_a3 → oBpu6kOuBIOwABE5kiB8V}/_buildManifest.js +0 -0
  260. /package/.next/static/{cBZM18sgaGtUo-xccD_a3 → oBpu6kOuBIOwABE5kiB8V}/_clientMiddlewareManifest.js +0 -0
  261. /package/.next/static/{cBZM18sgaGtUo-xccD_a3 → oBpu6kOuBIOwABE5kiB8V}/_ssgManifest.js +0 -0
@@ -0,0 +1,100 @@
1
+ /* Round 492 verification: chrome-strip Ring/Grid buttons gain
2
+ * `active:scale-95` press feedback alongside R196's `active:bg-cyan-
3
+ * 500/25` color-deepen. Adds haptic-like compression on click,
4
+ * synced with bg/color via inline `transform 150ms ease-out`.
5
+ *
6
+ * Verifies (per Ring + Grid button):
7
+ * 1. button DOM element present
8
+ * 2. className contains 'active:scale-95' and 'transform-gpu'
9
+ * 3. inline style transition string includes 'transform 150ms ease-out'
10
+ * 4. baseline transform resolves to none/matrix-identity (not pressed)
11
+ * 5. source-file regex confirms both buttons wired
12
+ */
13
+ import { chromium } from 'playwright';
14
+ import { readFileSync } from 'node:fs';
15
+
16
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
17
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
18
+
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(() => {
23
+ try {
24
+ localStorage.setItem('anet-theme', 'cyber');
25
+ localStorage.setItem('anet-topo-layout', 'ring');
26
+ sessionStorage.setItem('anet_v3_auth', '1');
27
+ } 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, 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', 'working')] } });
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-chrome-layout="ring"]', { timeout: 15000 });
45
+ await page.waitForTimeout(800);
46
+
47
+ const probe = async (selector) => {
48
+ const el = await page.$(selector);
49
+ if (!el) return null;
50
+ const data = await page.evaluate((selector) => {
51
+ const el = document.querySelector(selector);
52
+ if (!el) return null;
53
+ const cs = window.getComputedStyle(el);
54
+ return {
55
+ className: el.className || '',
56
+ transition_property: cs.transitionProperty,
57
+ transition_duration: cs.transitionDuration,
58
+ transition_timing_func: cs.transitionTimingFunction,
59
+ transform_baseline: cs.transform,
60
+ };
61
+ }, selector);
62
+ return data;
63
+ };
64
+
65
+ const ringInfo = await probe('[data-topo-chrome-layout="ring"]');
66
+ const gridInfo = await probe('[data-topo-chrome-layout="grid"]');
67
+
68
+ await browser.close();
69
+
70
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
71
+ const sourceRingActive = /data-topo-chrome-layout="ring"[\s\S]{0,8000}active:scale-95 transform-gpu/.test(src);
72
+ const sourceGridActive = /data-topo-chrome-layout="grid"[\s\S]{0,8000}active:scale-95 transform-gpu/.test(src);
73
+ const sourceRingTransform = /background-color 150ms ease, color 150ms ease, letter-spacing 200ms ease-out, transform 150ms ease-out/.test(src);
74
+ const sourceGridTransform = /border-color 200ms ease-out, letter-spacing 200ms ease-out, transform 150ms ease-out/.test(src);
75
+
76
+ const hasActiveScale = (cls) => /active:scale-95/.test(cls) && /transform-gpu/.test(cls);
77
+ const hasTransformInTransition = (info) =>
78
+ info && /transform/i.test(info.transition_property || '') && /\b0\.15s\b/.test(info.transition_duration || '');
79
+ const isBaselineIdentity = (info) =>
80
+ info && (info.transform_baseline === 'none' || info.transform_baseline === 'matrix(1, 0, 0, 1, 0, 0)');
81
+
82
+ const results = {
83
+ ring_dom_found: !!ringInfo,
84
+ ring_class_active: ringInfo && hasActiveScale(ringInfo.className),
85
+ ring_transform_tr: hasTransformInTransition(ringInfo),
86
+ ring_baseline_id: isBaselineIdentity(ringInfo),
87
+ grid_dom_found: !!gridInfo,
88
+ grid_class_active: gridInfo && hasActiveScale(gridInfo.className),
89
+ grid_transform_tr: hasTransformInTransition(gridInfo),
90
+ grid_baseline_id: isBaselineIdentity(gridInfo),
91
+ source_ring_active: sourceRingActive,
92
+ source_grid_active: sourceGridActive,
93
+ source_ring_tr: sourceRingTransform,
94
+ source_grid_tr: sourceGridTransform,
95
+ };
96
+ const ok = Object.values(results).every(Boolean);
97
+ console.log(`${ok ? '✅' : '❌'} chrome-strip Ring/Grid active:scale-95 (R492):`, JSON.stringify(results),
98
+ '\n ring:', ringInfo && { tp: ringInfo.transition_property, td: ringInfo.transition_duration, tx: ringInfo.transform_baseline },
99
+ '\n grid:', gridInfo && { tp: gridInfo.transition_property, td: gridInfo.transition_duration, tx: gridInfo.transform_baseline });
100
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,80 @@
1
+ /* Round 512 verification: root svg surfaces `data-topo-cluster-count`
2
+ * (14th attr in canvas state surface set). Paired with R469 fleet
3
+ * numerics + R502 categorical density.
4
+ *
5
+ * Contract:
6
+ * - grid layout: data-topo-cluster-count = groupBoxes.length (≥ 1)
7
+ * - ring layout: data-topo-cluster-count = '0' (groupBoxes is empty)
8
+ * - orphan-band fixture: cluster count includes the orphan band
9
+ *
10
+ * Tests across 2 fixtures × 2 layouts.
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({ layout, sessions, label }) {
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((l) => {
23
+ try {
24
+ localStorage.setItem('anet-theme', 'cyber');
25
+ localStorage.setItem('anet-topo-layout', l);
26
+ sessionStorage.setItem('anet_v3_auth', '1');
27
+ } catch {}
28
+ }, layout);
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: sessions.map(s => mk(s.alias, s.status)) } });
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('svg[data-topo-cluster-count]', { timeout: 15000 });
45
+ await page.waitForTimeout(1500);
46
+ const count = await page.evaluate(() =>
47
+ document.querySelector('svg[viewBox="0 0 1000 680"]')?.getAttribute('data-topo-cluster-count')
48
+ );
49
+ await browser.close();
50
+ return { label, count };
51
+ }
52
+
53
+ // Fixture A: 2 prefix groups (alpha×3 + beta×2) + 3 orphans
54
+ const fixtureA = [
55
+ { alias: 'alpha·1', status: 'working' },
56
+ { alias: 'alpha·2', status: 'idle' },
57
+ { alias: 'alpha·3', status: 'idle' },
58
+ { alias: 'beta·1', status: 'working' },
59
+ { alias: 'beta·2', status: 'idle' },
60
+ { alias: 'zeta', status: 'idle' },
61
+ { alias: 'omega', status: 'idle' },
62
+ { alias: 'lonely', status: 'idle' },
63
+ ];
64
+
65
+ const aGrid = await probe({ layout: 'grid', sessions: fixtureA, label: 'grid+2prefix+3orph' });
66
+ const aRing = await probe({ layout: 'ring', sessions: fixtureA, label: 'ring+same fixture' });
67
+
68
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
69
+ const sourceWired = /data-topo-cluster-count=\{groupBoxes\.length\}/.test(src);
70
+
71
+ const results = {
72
+ grid_returns_3: aGrid.count === '3', // 2 prefix groups + 1 orphan band
73
+ ring_returns_0: aRing.count === '0', // no group boxes in ring layout
74
+ source_wired: sourceWired,
75
+ };
76
+ const ok = Object.values(results).every(Boolean);
77
+ console.log(`${ok ? '✅' : '❌'} R512 cluster-count attr:`, JSON.stringify(results),
78
+ '\n grid:', JSON.stringify(aGrid),
79
+ '\n ring:', JSON.stringify(aRing));
80
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,104 @@
1
+ /* Round 528 verification: brand crescent gains ambient SMIL breath
2
+ * (0.8↔1.0 fill-opacity, 7s, repeat indefinite). 呼吸感 family 4th
3
+ * anchor — symmetric to R519 watermark breath but at 7s vs 6s cadence
4
+ * so the two ambient anchors don't beat together visibly.
5
+ *
6
+ * Test phases:
7
+ * 1. motion enabled: data-topo-brand-canvas-mark-breath='true'
8
+ * + <animate> child mounted on inner <rect>
9
+ * + values='0.8;1;0.8', dur='7s', repeatCount='indefinite'
10
+ * + attributeName='fill-opacity' (NOT 'opacity')
11
+ * 2. reduced-motion: breath attr='false', NO <animate> child
12
+ * 3. wrapper recede + visibility still works (regression check)
13
+ * 4. source-side regex confirms wiring
14
+ */
15
+ import { chromium } from 'playwright';
16
+ import { readFileSync } from 'node:fs';
17
+
18
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
19
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
20
+
21
+ async function probe(reducedMotion) {
22
+ const browser = await chromium.launch({ headless: true });
23
+ const ctx = await browser.newContext({
24
+ viewport: { width: 1500, height: 1200 },
25
+ reducedMotion: reducedMotion ? 'reduce' : 'no-preference',
26
+ });
27
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
28
+ await ctx.addInitScript(() => {
29
+ try {
30
+ localStorage.setItem('anet-theme', 'cyber');
31
+ localStorage.setItem('anet-topo-layout', 'ring');
32
+ sessionStorage.setItem('anet_v3_auth', '1');
33
+ } catch {}
34
+ });
35
+ await ctx.route('**/api/hub/status*', async (route) => {
36
+ const r = await route.fetch();
37
+ const b = await r.json();
38
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
39
+ const mk = (alias) => ({
40
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli',
41
+ network_id: nid, project_dir: null,
42
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
43
+ });
44
+ await route.fulfill({ response: r, json: { ...b, sessions: [
45
+ mk('alpha·1'), mk('alpha·2'),
46
+ ] } });
47
+ });
48
+ // NO messages → flowLinks=0 → crescent visible
49
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
50
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
51
+ const page = await ctx.newPage();
52
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
53
+ await page.waitForSelector('[data-topo-brand-canvas-mark]', { timeout: 15000 });
54
+ await page.waitForTimeout(800);
55
+ const data = await page.evaluate(() => {
56
+ const wrap = document.querySelector('[data-topo-brand-canvas-mark]');
57
+ const rect = wrap?.querySelector('rect[mask*="canvas-corner-mask"]');
58
+ const animate = rect?.querySelector('animate');
59
+ return {
60
+ breathAttr: wrap?.getAttribute('data-topo-brand-canvas-mark-breath'),
61
+ visibleAttr: wrap?.getAttribute('data-topo-brand-canvas-mark-visible'),
62
+ hasAnimate: !!animate,
63
+ attrName: animate?.getAttribute('attributeName') || null,
64
+ values: animate?.getAttribute('values') || null,
65
+ dur: animate?.getAttribute('dur') || null,
66
+ repeatCount: animate?.getAttribute('repeatCount') || null,
67
+ };
68
+ });
69
+ await browser.close();
70
+ return data;
71
+ }
72
+
73
+ const normal = await probe(false);
74
+ const reduced = await probe(true);
75
+
76
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
77
+ const sourceAnimateGated =
78
+ /\{!reducedMotion && \(\s*<animate attributeName="fill-opacity" values="0\.8;1;0\.8" dur="7s" repeatCount="indefinite" \/>\s*\)\}/.test(src);
79
+ const sourceAttrWired =
80
+ /data-topo-brand-canvas-mark-breath=\{reducedMotion \? 'false' : 'true'\}/.test(src);
81
+
82
+ const results = {
83
+ // motion enabled
84
+ normal_breath_true: normal?.breathAttr === 'true',
85
+ normal_visible_true: normal?.visibleAttr === 'true',
86
+ normal_has_animate: normal?.hasAnimate === true,
87
+ normal_attr_fill_op: normal?.attrName === 'fill-opacity',
88
+ normal_values_correct: normal?.values === '0.8;1;0.8',
89
+ normal_dur_7s: normal?.dur === '7s',
90
+ normal_repeat_indef: normal?.repeatCount === 'indefinite',
91
+ // prefers-reduced-motion
92
+ reduced_breath_false: reduced?.breathAttr === 'false',
93
+ reduced_no_animate: reduced?.hasAnimate === false,
94
+ reduced_visible_true: reduced?.visibleAttr === 'true', // crescent still visible, just no breath
95
+ // source
96
+ source_animate_gated: sourceAnimateGated,
97
+ source_attr_wired: sourceAttrWired,
98
+ };
99
+ const ok = Object.values(results).every(Boolean);
100
+ console.log(`${ok ? '✅' : '❌'} R528 crescent ambient breath:`,
101
+ JSON.stringify(results, null, 2),
102
+ '\n normal:', JSON.stringify(normal),
103
+ '\n reduced:', JSON.stringify(reduced));
104
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,111 @@
1
+ /* Round 526 verification: brand crescent at canvas top-left gains
2
+ * focal-recede (4th anchor in family). Multiplies visible opacity by
3
+ * 0.7 when non-hub canvas surface is hovered.
4
+ *
5
+ * Test phases (flowLinks=0 state — crescent visible):
6
+ * 1. rest: opacity=0.35, recede attr='false', visible='true'
7
+ * 2. hover legend `idle` row: opacity=0.245 (0.35 × 0.7),
8
+ * recede attr='true'
9
+ * 3. mouseleave: opacity returns to 0.35, recede='false'
10
+ * 4. source-side regex confirms multiplicative opacity + attr wired
11
+ */
12
+ import { chromium } from 'playwright';
13
+ import { readFileSync } from 'node:fs';
14
+
15
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
16
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
17
+
18
+ const browser = await chromium.launch({ headless: true });
19
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
20
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
21
+ await ctx.addInitScript(() => {
22
+ try {
23
+ localStorage.setItem('anet-theme', 'cyber');
24
+ localStorage.setItem('anet-topo-layout', 'ring');
25
+ sessionStorage.setItem('anet_v3_auth', '1');
26
+ } catch {}
27
+ });
28
+ await ctx.route('**/api/hub/status*', async (route) => {
29
+ const r = await route.fetch();
30
+ const b = await r.json();
31
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
32
+ const mk = (alias) => ({
33
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli',
34
+ network_id: nid, project_dir: null,
35
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
36
+ });
37
+ await route.fulfill({ response: r, json: { ...b, sessions: [
38
+ mk('alpha·1'), mk('alpha·2'), mk('alpha·3'),
39
+ ] } });
40
+ });
41
+ // NO messages → flowLinks.length === 0 → crescent visible at opacity 0.35
42
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
43
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
44
+ const page = await ctx.newPage();
45
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
46
+ await page.waitForSelector('[data-topo-brand-canvas-mark]', { timeout: 15000 });
47
+ await page.waitForTimeout(800);
48
+
49
+ // Phase 1: rest
50
+ const rest = await page.evaluate(() => {
51
+ const m = document.querySelector('[data-topo-brand-canvas-mark]');
52
+ return {
53
+ opacity: parseFloat(m?.getAttribute('opacity') || '0'),
54
+ visible: m?.getAttribute('data-topo-brand-canvas-mark-visible'),
55
+ recede: m?.getAttribute('data-topo-brand-canvas-mark-recede'),
56
+ };
57
+ });
58
+
59
+ // Phase 2: hover legend `idle` row (sets hoveredStatus → recede)
60
+ await page.hover('[data-legend-row-label="idle"]');
61
+ await page.waitForTimeout(400);
62
+ const hover = await page.evaluate(() => {
63
+ const m = document.querySelector('[data-topo-brand-canvas-mark]');
64
+ return {
65
+ opacity: parseFloat(m?.getAttribute('opacity') || '0'),
66
+ visible: m?.getAttribute('data-topo-brand-canvas-mark-visible'),
67
+ recede: m?.getAttribute('data-topo-brand-canvas-mark-recede'),
68
+ };
69
+ });
70
+
71
+ // Phase 3: mouseleave
72
+ await page.mouse.move(900, 50);
73
+ await page.waitForTimeout(400);
74
+ const leave = await page.evaluate(() => {
75
+ const m = document.querySelector('[data-topo-brand-canvas-mark]');
76
+ return {
77
+ opacity: parseFloat(m?.getAttribute('opacity') || '0'),
78
+ recede: m?.getAttribute('data-topo-brand-canvas-mark-recede'),
79
+ };
80
+ });
81
+
82
+ await browser.close();
83
+
84
+ // Source regex
85
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
86
+ const sourceOpacityWired =
87
+ /opacity=\{\(flowLinks\.length === 0 \? 0\.35 : 0\) \* \(\s*\(hoveredAlias \|\| hoveredEdgeKey \|\| hoveredGroupLabel \|\|\s*hoveredStatus \|\| hoveredVendor\) && !hoveredHub \? 0\.7 : 1\s*\)\}/.test(src);
88
+ const sourceAttrWired =
89
+ /data-topo-brand-canvas-mark-recede=\{\s*\(hoveredAlias \|\| hoveredEdgeKey \|\| hoveredGroupLabel \|\|\s*hoveredStatus \|\| hoveredVendor\) && !hoveredHub \? 'true' : 'false'\s*\}/.test(src);
90
+
91
+ const approxEq = (a, b, tol = 0.001) => Math.abs(a - b) < tol;
92
+
93
+ const results = {
94
+ rest_opacity_035: approxEq(rest?.opacity, 0.35),
95
+ rest_visible_true: rest?.visible === 'true',
96
+ rest_recede_false: rest?.recede === 'false',
97
+ hover_opacity_0245: approxEq(hover?.opacity, 0.245),
98
+ hover_recede_true: hover?.recede === 'true',
99
+ hover_visible_still: hover?.visible === 'true', // crescent should still be visible
100
+ leave_opacity_035: approxEq(leave?.opacity, 0.35),
101
+ leave_recede_false: leave?.recede === 'false',
102
+ source_opacity_wired: sourceOpacityWired,
103
+ source_attr_wired: sourceAttrWired,
104
+ };
105
+ const ok = Object.values(results).every(Boolean);
106
+ console.log(`${ok ? '✅' : '❌'} R526 crescent focal-recede:`,
107
+ JSON.stringify(results, null, 2),
108
+ '\n rest:', JSON.stringify(rest),
109
+ '\n hover:', JSON.stringify(hover),
110
+ '\n leave:', JSON.stringify(leave));
111
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,90 @@
1
+ /* Round 534 verification: edge midpoint badge gains drop-shadow glow on
2
+ * hover/pin (cyan accent), with hover precedence over isHot (amber R480).
3
+ *
4
+ * Test strategy: source-side wiring is canonical because edge hover via
5
+ * Playwright is impractical (topo-panel rect intercepts at SVG root;
6
+ * R48 hitbox is the React handler target but isn't directly hoverable
7
+ * through the panel). Rest-state DOM probe + source regex covers the
8
+ * 4-axis hover-lift parity wiring.
9
+ *
10
+ * Test phases:
11
+ * 1. rest cold: glow attr='false', filter='none', lifted attr='false'
12
+ * (regression check — pre-R534 behavior unchanged at rest)
13
+ * 2. source-side regex confirms:
14
+ * - filter ternary precedence: (hovered||pinned) > isHot > undefined
15
+ * - cyan accent hue (pal.legendAccent) at 99 hex alpha
16
+ * - data-edge-badge-glow 3-value attr ('hover' | 'hot' | 'false')
17
+ * - transition list includes filter
18
+ */
19
+ import { chromium } from 'playwright';
20
+ import { readFileSync } from 'node:fs';
21
+
22
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
23
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
24
+
25
+ const browser = await chromium.launch({ headless: true });
26
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
27
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
28
+ await ctx.addInitScript(() => {
29
+ try {
30
+ localStorage.setItem('anet-theme', 'cyber');
31
+ localStorage.setItem('anet-topo-layout', 'ring');
32
+ sessionStorage.setItem('anet_v3_auth', '1');
33
+ } catch {}
34
+ });
35
+ await ctx.route('**/api/hub/status*', async (route) => {
36
+ const r = await route.fetch();
37
+ const b = await r.json();
38
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
39
+ const mk = (alias) => ({
40
+ alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
41
+ network_id: nid, project_dir: null,
42
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
43
+ });
44
+ await route.fulfill({ response: r, json: { ...b, sessions: [mk('a·1'), mk('a·2')] } });
45
+ });
46
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: {
47
+ messages: [{ id: 'm1', from_alias: 'a·1', to_alias: 'a·2', content: 'test', created_at: fresh }]
48
+ } }));
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-edge-badge-glow]', { timeout: 15000 });
53
+ await page.waitForTimeout(800);
54
+
55
+ const rest = await page.evaluate(() => {
56
+ const el = document.querySelector('[data-edge-badge-glow]');
57
+ if (!el) return null;
58
+ const cs = getComputedStyle(el);
59
+ return {
60
+ glowAttr: el.getAttribute('data-edge-badge-glow'),
61
+ liftedAttr: el.getAttribute('data-edge-badge-lifted'),
62
+ filter: cs.filter,
63
+ transition: cs.transition,
64
+ };
65
+ });
66
+
67
+ await browser.close();
68
+
69
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
70
+ const sourceFilterTernary =
71
+ /filter: \(isHoveredEdge \|\| isPinned\)\s+\? `drop-shadow\(0 0 3px \$\{pal\.legendAccent\}99\)`\s+: isHot\s+\? `drop-shadow\(0 0 3px \$\{hotStroke\}80\)`\s+: undefined,/.test(src);
72
+ const sourceAttrTernary =
73
+ /data-edge-badge-glow=\{\(isHoveredEdge \|\| isPinned\) \? 'hover' : isHot \? 'hot' : 'false'\}/.test(src);
74
+ const sourceTransitionFilter =
75
+ /transition: 'r 180ms ease-out, stroke 300ms ease-out, stroke-width 300ms ease-out, fill 200ms ease-out, opacity 200ms ease-out, filter 200ms ease-out'/.test(src);
76
+
77
+ const results = {
78
+ rest_glow_false: rest?.glowAttr === 'false',
79
+ rest_lifted_false: rest?.liftedAttr === 'false',
80
+ rest_filter_none: rest?.filter === 'none' || rest?.filter === '',
81
+ rest_transition_has_filter: /\bfilter\b/.test(rest?.transition || ''),
82
+ source_filter_ternary: sourceFilterTernary,
83
+ source_attr_ternary: sourceAttrTernary,
84
+ source_transition_filter: sourceTransitionFilter,
85
+ };
86
+ const ok = Object.values(results).every(Boolean);
87
+ console.log(`${ok ? '✅' : '❌'} R534 edge-badge hover-glow (source-canonical):`,
88
+ JSON.stringify(results, null, 2),
89
+ '\n rest:', JSON.stringify(rest));
90
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,83 @@
1
+ /* Round 570 verification: edge-badge digit gains filter brightness
2
+ * (1.15) on (isHoveredEdge || isPinned || isHot). Joins per-element
3
+ * brightness family at 7th anchor.
4
+ *
5
+ * Test phases:
6
+ * 1. mock 1 message → 1 flowLink with edge-badge rendered
7
+ * 2. rest: filter='none', brightness-attr='1'
8
+ * 3. transition-property contains 'filter'
9
+ * 4. source-side regex confirms wiring
10
+ *
11
+ * Live hover/pin on SVG edge-badge is unreliable for Playwright
12
+ * (R525 banked); use source-canonical for gate + rest data-attrs.
13
+ */
14
+ import { chromium } from 'playwright';
15
+ import { readFileSync } from 'node:fs';
16
+
17
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
18
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
19
+
20
+ const browser = await chromium.launch({ headless: true });
21
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
22
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
23
+ await ctx.addInitScript(() => {
24
+ try {
25
+ localStorage.setItem('anet-theme', 'cyber');
26
+ localStorage.setItem('anet-topo-layout', 'ring');
27
+ sessionStorage.setItem('anet_v3_auth', '1');
28
+ } 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('a·1'), mk('a·2')] } });
40
+ });
41
+ // 1 message so flowLinks > 0 → edge-badge renders
42
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [
43
+ { from_alias: 'a·1', to_alias: 'a·2', content: 'hi', created_at: fresh },
44
+ ] } }));
45
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
46
+ const page = await ctx.newPage();
47
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
48
+ await page.waitForSelector('[data-edge-badge-text]', { timeout: 15000 });
49
+ await page.waitForTimeout(500);
50
+
51
+ const rest = await page.evaluate(() => {
52
+ const el = document.querySelector('[data-edge-badge-text]');
53
+ if (!el) return null;
54
+ const cs = getComputedStyle(el);
55
+ return {
56
+ filter: cs.filter,
57
+ transitionProperty: cs.transitionProperty,
58
+ brightnessAttr: el.getAttribute('data-edge-badge-text-brightness'),
59
+ pinAttr: el.getAttribute('data-edge-badge-text-pin'),
60
+ };
61
+ });
62
+
63
+ await browser.close();
64
+
65
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
66
+ const sourceFilter = /filter: \(isHoveredEdge \|\| isPinned \|\| isHot\)\s*\?\s*'brightness\(1\.15\)'\s*:\s*undefined/.test(src);
67
+ const sourceAttr = /data-edge-badge-text-brightness=\{\(isHoveredEdge \|\| isPinned \|\| isHot\) \? '1\.15' : '1'\}/.test(src);
68
+ const sourceTransition = /transition: 'letter-spacing 300ms ease-out, font-weight 300ms ease-out, filter 300ms ease-out'/.test(src);
69
+
70
+ const results = {
71
+ rest_filter_none: rest?.filter === 'none',
72
+ rest_brightness_1: rest?.brightnessAttr === '1',
73
+ rest_pin_false: rest?.pinAttr === 'false',
74
+ transition_has_filter: /filter/.test(rest?.transitionProperty || ''),
75
+ source_filter: sourceFilter,
76
+ source_attr: sourceAttr,
77
+ source_transition: sourceTransition,
78
+ };
79
+ const ok = Object.values(results).every(Boolean);
80
+ console.log(`${ok ? '✅' : '❌'} R570 edge-badge text brightness(1.15) (per-element family 7th anchor):`,
81
+ JSON.stringify(results, null, 2),
82
+ `\n rest: ${JSON.stringify(rest)}`);
83
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,82 @@
1
+ /* Round 583 verification: edge flow particle gains filter
2
+ * brightness(1.15) on edge hover/endpoint hover. 22nd anchor in
3
+ * per-element brightness family.
4
+ *
5
+ * Test phases:
6
+ * 1. mock 1 message → 1 flowLink → particle renders (SMIL-animated)
7
+ * 2. rest: filter='none', brightness-attr='1'
8
+ * 3. transition-property contains 'filter'
9
+ * 4. source: filter conditional + data-attr + transition extension
10
+ */
11
+ import { chromium } from 'playwright';
12
+ import { readFileSync } from 'node:fs';
13
+
14
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
15
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
16
+
17
+ const browser = await chromium.launch({ headless: true });
18
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
19
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
20
+ await ctx.addInitScript(() => {
21
+ try {
22
+ localStorage.setItem('anet-theme', 'cyber');
23
+ localStorage.setItem('anet-topo-layout', 'ring');
24
+ sessionStorage.setItem('anet_v3_auth', '1');
25
+ } catch {}
26
+ });
27
+ await ctx.route('**/api/hub/status*', async (route) => {
28
+ const r = await route.fetch();
29
+ const b = await r.json();
30
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
31
+ const mk = (alias) => ({
32
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'claude-code-cli',
33
+ network_id: nid, project_dir: null,
34
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
35
+ });
36
+ await route.fulfill({ response: r, json: { ...b, sessions: [mk('a·1'), mk('a·2')] } });
37
+ });
38
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [
39
+ { from_alias: 'a·1', to_alias: 'a·2', content: 'hi', created_at: fresh },
40
+ ] } }));
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-edge-particle]', { timeout: 15000, state: 'attached' });
45
+ await page.waitForTimeout(500);
46
+
47
+ const rest = await page.evaluate(() => {
48
+ const el = document.querySelector('[data-edge-particle]');
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-edge-particle-brightness'),
55
+ liftedAttr: el.getAttribute('data-edge-particle-lifted'),
56
+ };
57
+ });
58
+
59
+ await browser.close();
60
+
61
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
62
+ const sourceFilter = /filter: \(isHoveredEdge \|\| isEndpointHoveredEdge\)\s*\?\s*\(isLight \? 'brightness\(1\.15\)' : 'url\(#topo-glow\) brightness\(1\.15\)'\)\s*:\s*undefined/.test(src);
63
+ const sourceAttr = /data-edge-particle-brightness=\{\(isHoveredEdge \|\| isEndpointHoveredEdge\) \? '1\.15' : '1'\}/.test(src);
64
+ const sourceTransition = /transition: 'fill 200ms ease-out, opacity 200ms ease-out, r 200ms ease-out, filter 200ms ease-out'/.test(src);
65
+
66
+ const results = {
67
+ particle_present: !!rest,
68
+ // On cyber theme at rest, particle inherits url(#topo-glow) via attribute
69
+ rest_filter_has_url_or_none: /url\(/.test(rest?.filter || '') || rest?.filter === 'none',
70
+ rest_filter_no_brightness: !/brightness\(/.test(rest?.filter || ''),
71
+ rest_brightness_1: rest?.brightnessAttr === '1',
72
+ rest_lifted_false: rest?.liftedAttr === 'false',
73
+ transition_has_filter: /filter/.test(rest?.transitionProperty || ''),
74
+ source_filter: sourceFilter,
75
+ source_attr: sourceAttr,
76
+ source_transition: sourceTransition,
77
+ };
78
+ const ok = Object.values(results).every(Boolean);
79
+ console.log(`${ok ? '✅' : '❌'} R583 edge flow particle brightness (22nd anchor):`,
80
+ JSON.stringify(results, null, 2),
81
+ `\n rest: ${JSON.stringify(rest)}`);
82
+ process.exit(ok ? 0 : 1);