@sleep2agi/agent-network-dashboard 0.5.2-preview.0 → 0.5.2-preview.10

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 (154) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +3 -3
  3. package/.next/diagnostics/route-bundle-stats.json +7 -7
  4. package/.next/fallback-build-manifest.json +3 -3
  5. package/.next/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.html +1 -1
  13. package/.next/server/app/_not-found.rsc +1 -1
  14. package/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  15. package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  16. package/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  17. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  18. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  19. package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  20. package/.next/server/app/admin.html +1 -1
  21. package/.next/server/app/admin.rsc +1 -1
  22. package/.next/server/app/admin.segments/_full.segment.rsc +1 -1
  23. package/.next/server/app/admin.segments/_head.segment.rsc +1 -1
  24. package/.next/server/app/admin.segments/_index.segment.rsc +1 -1
  25. package/.next/server/app/admin.segments/_tree.segment.rsc +1 -1
  26. package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +1 -1
  27. package/.next/server/app/admin.segments/admin.segment.rsc +1 -1
  28. package/.next/server/app/index.html +2 -2
  29. package/.next/server/app/index.rsc +2 -2
  30. package/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  31. package/.next/server/app/index.segments/_full.segment.rsc +2 -2
  32. package/.next/server/app/index.segments/_head.segment.rsc +1 -1
  33. package/.next/server/app/index.segments/_index.segment.rsc +1 -1
  34. package/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  35. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  36. package/.next/server/app/login.html +2 -2
  37. package/.next/server/app/login.rsc +2 -2
  38. package/.next/server/app/login.segments/_full.segment.rsc +2 -2
  39. package/.next/server/app/login.segments/_head.segment.rsc +1 -1
  40. package/.next/server/app/login.segments/_index.segment.rsc +1 -1
  41. package/.next/server/app/login.segments/_tree.segment.rsc +1 -1
  42. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +2 -2
  43. package/.next/server/app/login.segments/login.segment.rsc +1 -1
  44. package/.next/server/app/logs.html +1 -1
  45. package/.next/server/app/logs.rsc +1 -1
  46. package/.next/server/app/logs.segments/_full.segment.rsc +1 -1
  47. package/.next/server/app/logs.segments/_head.segment.rsc +1 -1
  48. package/.next/server/app/logs.segments/_index.segment.rsc +1 -1
  49. package/.next/server/app/logs.segments/_tree.segment.rsc +1 -1
  50. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +1 -1
  51. package/.next/server/app/logs.segments/logs.segment.rsc +1 -1
  52. package/.next/server/app/messages.html +1 -1
  53. package/.next/server/app/messages.rsc +1 -1
  54. package/.next/server/app/messages.segments/_full.segment.rsc +1 -1
  55. package/.next/server/app/messages.segments/_head.segment.rsc +1 -1
  56. package/.next/server/app/messages.segments/_index.segment.rsc +1 -1
  57. package/.next/server/app/messages.segments/_tree.segment.rsc +1 -1
  58. package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +1 -1
  59. package/.next/server/app/messages.segments/messages.segment.rsc +1 -1
  60. package/.next/server/app/node.html +1 -1
  61. package/.next/server/app/node.rsc +1 -1
  62. package/.next/server/app/node.segments/_full.segment.rsc +1 -1
  63. package/.next/server/app/node.segments/_head.segment.rsc +1 -1
  64. package/.next/server/app/node.segments/_index.segment.rsc +1 -1
  65. package/.next/server/app/node.segments/_tree.segment.rsc +1 -1
  66. package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +1 -1
  67. package/.next/server/app/node.segments/node.segment.rsc +1 -1
  68. package/.next/server/app/nodes.html +1 -1
  69. package/.next/server/app/nodes.rsc +1 -1
  70. package/.next/server/app/nodes.segments/_full.segment.rsc +1 -1
  71. package/.next/server/app/nodes.segments/_head.segment.rsc +1 -1
  72. package/.next/server/app/nodes.segments/_index.segment.rsc +1 -1
  73. package/.next/server/app/nodes.segments/_tree.segment.rsc +1 -1
  74. package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +1 -1
  75. package/.next/server/app/nodes.segments/nodes.segment.rsc +1 -1
  76. package/.next/server/app/page_client-reference-manifest.js +1 -1
  77. package/.next/server/app/server-logs.html +1 -1
  78. package/.next/server/app/server-logs.rsc +1 -1
  79. package/.next/server/app/server-logs.segments/_full.segment.rsc +1 -1
  80. package/.next/server/app/server-logs.segments/_head.segment.rsc +1 -1
  81. package/.next/server/app/server-logs.segments/_index.segment.rsc +1 -1
  82. package/.next/server/app/server-logs.segments/_tree.segment.rsc +1 -1
  83. package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +1 -1
  84. package/.next/server/app/server-logs.segments/server-logs.segment.rsc +1 -1
  85. package/.next/server/app/settings/networks.html +1 -1
  86. package/.next/server/app/settings/networks.rsc +1 -1
  87. package/.next/server/app/settings/networks.segments/_full.segment.rsc +1 -1
  88. package/.next/server/app/settings/networks.segments/_head.segment.rsc +1 -1
  89. package/.next/server/app/settings/networks.segments/_index.segment.rsc +1 -1
  90. package/.next/server/app/settings/networks.segments/_tree.segment.rsc +1 -1
  91. package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +1 -1
  92. package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +1 -1
  93. package/.next/server/app/settings/networks.segments/settings.segment.rsc +1 -1
  94. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  95. package/.next/server/app/settings/tokens.html +1 -1
  96. package/.next/server/app/settings/tokens.rsc +1 -1
  97. package/.next/server/app/settings/tokens.segments/_full.segment.rsc +1 -1
  98. package/.next/server/app/settings/tokens.segments/_head.segment.rsc +1 -1
  99. package/.next/server/app/settings/tokens.segments/_index.segment.rsc +1 -1
  100. package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +1 -1
  101. package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +1 -1
  102. package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +1 -1
  103. package/.next/server/app/settings/tokens.segments/settings.segment.rsc +1 -1
  104. package/.next/server/app/settings.html +2 -2
  105. package/.next/server/app/settings.rsc +2 -2
  106. package/.next/server/app/settings.segments/_full.segment.rsc +2 -2
  107. package/.next/server/app/settings.segments/_head.segment.rsc +1 -1
  108. package/.next/server/app/settings.segments/_index.segment.rsc +1 -1
  109. package/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
  110. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +2 -2
  111. package/.next/server/app/settings.segments/settings.segment.rsc +1 -1
  112. package/.next/server/app/tasks.html +1 -1
  113. package/.next/server/app/tasks.rsc +1 -1
  114. package/.next/server/app/tasks.segments/_full.segment.rsc +1 -1
  115. package/.next/server/app/tasks.segments/_head.segment.rsc +1 -1
  116. package/.next/server/app/tasks.segments/_index.segment.rsc +1 -1
  117. package/.next/server/app/tasks.segments/_tree.segment.rsc +1 -1
  118. package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +1 -1
  119. package/.next/server/app/tasks.segments/tasks.segment.rsc +1 -1
  120. package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js +1 -1
  121. package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js.map +1 -1
  122. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +2 -2
  123. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
  124. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
  125. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
  126. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
  127. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
  128. package/.next/server/middleware-build-manifest.js +3 -3
  129. package/.next/server/pages/404.html +1 -1
  130. package/.next/server/pages/500.html +1 -1
  131. package/.next/static/chunks/0tio1iob9rlrz.js +1 -0
  132. package/.next/static/chunks/0xkn4hstf40ax.js +1 -0
  133. package/.next/static/chunks/{0p8xwrfjtykvn.js → 0z3uxdpbxx6vi.js} +1 -1
  134. package/.next/static/chunks/0z_jx0znc~hgn.js +4 -0
  135. package/.next/trace +2 -2
  136. package/.next/trace-build +1 -1
  137. package/app/components/TopoGraph.tsx +230 -10
  138. package/package.json +1 -1
  139. package/scripts/topo-group-fade-cadence-test.mjs +92 -0
  140. package/scripts/topo-group-label-glow-test.mjs +104 -0
  141. package/scripts/topo-hub-digit-glow-test.mjs +105 -0
  142. package/scripts/topo-layout-theme-attrs-test.mjs +91 -0
  143. package/scripts/topo-legend-pin-ring-glow-test.mjs +105 -0
  144. package/scripts/topo-legend-row-text-cadence-test.mjs +93 -0
  145. package/scripts/topo-legend-row-tint-cadence-test.mjs +92 -0
  146. package/scripts/topo-recent-row-freshness-glow-test.mjs +100 -0
  147. package/scripts/topo-recent-row-text-cadence-test.mjs +99 -0
  148. package/scripts/topo-recent-row-tint-cadence-test.mjs +96 -0
  149. package/.next/static/chunks/0caxil0dw-oe9.js +0 -4
  150. package/.next/static/chunks/0x.m3vy8e5iit.js +0 -1
  151. package/.next/static/chunks/12gq1w9k_7v06.js +0 -1
  152. /package/.next/static/{5IMzNtH5S5Oqe-FCw1CX4 → IKi4kxrMEeLozKLpLTtI7}/_buildManifest.js +0 -0
  153. /package/.next/static/{5IMzNtH5S5Oqe-FCw1CX4 → IKi4kxrMEeLozKLpLTtI7}/_clientMiddlewareManifest.js +0 -0
  154. /package/.next/static/{5IMzNtH5S5Oqe-FCw1CX4 → IKi4kxrMEeLozKLpLTtI7}/_ssgManifest.js +0 -0
@@ -0,0 +1,105 @@
1
+ /* Round 476 verification: hub working-count digit gains filter:
2
+ * drop-shadow glow on hub-hover. Stacks with the existing 4-axis
3
+ * hub-hover gesture (R209 scale + R425 fw + R253 fill + R213
4
+ * opacity). data-topo-hub-working-count-glow attr exposes the gate.
5
+ *
6
+ * Contract:
7
+ * - at rest (no hover): data-topo-hub-working-count-glow='false',
8
+ * filter is empty / 'none'
9
+ * - on mouseenter hub-core: glow='true', filter starts with
10
+ * 'drop-shadow'
11
+ * - source-file conditional wired (cyber + light theme variants)
12
+ * - transition list extends to include 'filter 200ms ease-out'
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, status) => ({
35
+ alias, status, 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
+ // Need at least one working session so the digit renders (R213 opacity gate)
40
+ await route.fulfill({ response: r, json: { ...b, sessions: [
41
+ mk('a·1', 'working'), mk('a·2', 'idle'), mk('a·3', 'working'),
42
+ ] } });
43
+ });
44
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
45
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
46
+
47
+ const page = await ctx.newPage();
48
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
49
+ await page.waitForSelector('[data-topo-hub-working-count]', { timeout: 15000 });
50
+ await page.waitForTimeout(500);
51
+
52
+ const readDigit = () => page.evaluate(() => {
53
+ const t = document.querySelector('[data-topo-hub-working-count]');
54
+ if (!t) return null;
55
+ const cs = getComputedStyle(t);
56
+ return {
57
+ glow: t.getAttribute('data-topo-hub-working-count-glow'),
58
+ count: t.getAttribute('data-topo-hub-working-count'),
59
+ filter: cs.filter,
60
+ transition: cs.transition,
61
+ };
62
+ });
63
+
64
+ const restProbe = await readDigit();
65
+
66
+ // Hover the hub via the hover-trigger surface (hub <g> or core)
67
+ await page.hover('[data-topo-hub-core]');
68
+ await page.waitForTimeout(400);
69
+ const hoverProbe = await readDigit();
70
+
71
+ // Leave hub
72
+ await page.mouse.move(2, 2);
73
+ await page.waitForTimeout(400);
74
+ const restAgainProbe = await readDigit();
75
+
76
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
77
+ const sourceGlowAttr = /data-topo-hub-working-count-glow=/.test(src);
78
+ const sourceDropShadow = /drop-shadow\(0 0 \dpx rgba\(/.test(src);
79
+ const sourceFilterTween = /filter 200ms ease-out/.test(src);
80
+
81
+ await browser.close();
82
+
83
+ const restGlowFalse = restProbe?.glow === 'false';
84
+ const hoverGlowTrue = hoverProbe?.glow === 'true';
85
+ const restAgainFalse = restAgainProbe?.glow === 'false';
86
+ // Computed filter — at rest should be 'none' or empty; on hover should
87
+ // contain 'drop-shadow'.
88
+ const restFilterEmpty = restProbe?.filter === 'none' || !restProbe?.filter;
89
+ const hoverFilterHasShadow = /drop-shadow/.test(hoverProbe?.filter || '');
90
+
91
+ const results = {
92
+ rest_glow_false: restGlowFalse,
93
+ hover_glow_true: hoverGlowTrue,
94
+ rest_again_false: restAgainFalse,
95
+ rest_filter_none: restFilterEmpty,
96
+ hover_filter_dropshadow: hoverFilterHasShadow,
97
+ source_glow_attr: sourceGlowAttr,
98
+ source_drop_shadow: sourceDropShadow,
99
+ source_filter_tween: sourceFilterTween,
100
+ };
101
+ const ok = Object.values(results).every(Boolean);
102
+ console.log(`${ok ? '✅' : '❌'} hub digit drop-shadow glow:`, JSON.stringify(results),
103
+ '\n rest:', JSON.stringify(restProbe),
104
+ '\n hover:', JSON.stringify(hoverProbe));
105
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,91 @@
1
+ /* Round 471 verification: root svg surfaces 2 canvas-level mode
2
+ * attrs (data-topo-layout + data-topo-theme). Completes the
3
+ * R462/R466/R467/R469 root-svg state surface set; canvas root
4
+ * now carries 9 cross-cutting attrs.
5
+ *
6
+ * Contract:
7
+ * - cyber theme + grid layout fixture →
8
+ * data-topo-layout='grid' + data-topo-theme='cyber'
9
+ * - source-file conditional wired
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
+ async function probe(theme, layout) {
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
+ // Playwright addInitScript takes only ONE arg — pass {theme, layout}
22
+ // as a struct instead of two positional params.
23
+ await ctx.addInitScript((arg) => {
24
+ try {
25
+ localStorage.setItem('anet-theme', arg.theme);
26
+ localStorage.setItem('anet-topo-layout', arg.layout);
27
+ sessionStorage.setItem('anet_v3_auth', '1');
28
+ } catch {}
29
+ }, { theme, layout });
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, status) => ({
35
+ alias, status, 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: [
40
+ mk('a·1', 'working'), mk('a·2', 'idle'),
41
+ ] } });
42
+ });
43
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
44
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
45
+ const page = await ctx.newPage();
46
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
47
+ await page.waitForSelector('svg[data-topo-layout]', { timeout: 15000 });
48
+ // Give React extra time to hydrate + run useEffects that restore
49
+ // layout/theme from localStorage (live probe showed ~2500ms).
50
+ await page.waitForTimeout(2800);
51
+ // Wait for the localStorage-restore useEffect (line 592-620) to
52
+ // run + React to commit the state — layout starts at 'ring' default
53
+ // and flips to the localStorage value on mount. The data-attr won't
54
+ // reflect the saved value until after the first effect tick. Live
55
+ // probe showed ~2500ms is enough.
56
+ await page.waitForFunction(
57
+ (expected) => document.querySelector('svg[viewBox="0 0 1000 680"]')?.getAttribute('data-topo-layout') === expected,
58
+ layout,
59
+ { timeout: 10000 },
60
+ ).catch(() => {});
61
+ const attrs = await page.evaluate(() => {
62
+ const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
63
+ return {
64
+ layout: svg?.getAttribute('data-topo-layout'),
65
+ theme: svg?.getAttribute('data-topo-theme'),
66
+ };
67
+ });
68
+ await browser.close();
69
+ return attrs;
70
+ }
71
+
72
+ const cyberGrid = await probe('cyber', 'grid');
73
+ const lightRing = await probe('light', 'ring');
74
+
75
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
76
+ const sourceLayout = /data-topo-layout=\{layout\}/.test(src);
77
+ const sourceTheme = /data-topo-theme=\{isLight \? 'light' : 'cyber'\}/.test(src);
78
+
79
+ const results = {
80
+ cyber_grid_layout: cyberGrid.layout === 'grid',
81
+ cyber_grid_theme: cyberGrid.theme === 'cyber',
82
+ light_ring_layout: lightRing.layout === 'ring',
83
+ light_ring_theme: lightRing.theme === 'light',
84
+ source_layout: sourceLayout,
85
+ source_theme: sourceTheme,
86
+ };
87
+ const ok = Object.values(results).every(Boolean);
88
+ console.log(`${ok ? '✅' : '❌'} svg layout+theme attrs:`, JSON.stringify(results),
89
+ '\n cyber+grid:', JSON.stringify(cyberGrid),
90
+ '\n light+ring:', JSON.stringify(lightRing));
91
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,105 @@
1
+ /* Round 477 verification: legend pin-ring (R181 always-mount stroke
2
+ * ring) gains filter: drop-shadow glow on isPinned. Extends R476's
3
+ * drop-shadow idiom from the hub-digit focal scope to the legend-
4
+ * row pin-state scope.
5
+ *
6
+ * Contract:
7
+ * - at rest (no pin): every legend-pin-ring has data-legend-pin-
8
+ * ring-glow='false' AND computed filter === 'none' AND opacity=0
9
+ * - click a legend status row to pin: that ring flips to
10
+ * glow='true' + filter starts with 'drop-shadow' + opacity=1
11
+ * - other (non-pinned) rings stay at rest
12
+ * - source-file conditional wired
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, status) => ({
35
+ alias, status, 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: [
40
+ mk('a·1', 'working'), mk('a·2', 'idle'), mk('a·3', 'offline'),
41
+ ] } });
42
+ });
43
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
44
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
45
+
46
+ const page = await ctx.newPage();
47
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
48
+ await page.waitForSelector('[data-legend-pin-ring]', { timeout: 15000 });
49
+ await page.waitForTimeout(500);
50
+
51
+ const readAll = () => page.evaluate(() => {
52
+ const rings = [...document.querySelectorAll('[data-legend-pin-ring]')];
53
+ return rings.map(r => {
54
+ const cs = getComputedStyle(r);
55
+ return {
56
+ key: r.getAttribute('data-legend-pin-ring'),
57
+ glow: r.getAttribute('data-legend-pin-ring-glow'),
58
+ pinned: r.getAttribute('data-legend-pin-ring-pinned'),
59
+ filter: cs.filter,
60
+ };
61
+ });
62
+ });
63
+
64
+ const restAll = await readAll();
65
+ // Click a legend row hitbox (R271 hitbox rect at y=row.y0-11..+11)
66
+ // Use data-legend-row-tinted as the hitbox handle — clicking it
67
+ // toggles pinnedStatus to its key.
68
+ const firstRow = await page.$('[data-legend-row-tinted]');
69
+ let pinnedAll = null;
70
+ if (firstRow) {
71
+ await firstRow.click();
72
+ await page.waitForTimeout(400);
73
+ pinnedAll = await readAll();
74
+ }
75
+
76
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
77
+ const sourceGlowAttr = /data-legend-pin-ring-glow=\{isPinned/.test(src);
78
+ const sourceDropShadow = /drop-shadow\(0 0 3px \$\{row\.fill\}88\)/.test(src);
79
+ const sourceFilterTween = /opacity 150ms ease-out, filter 200ms ease-out/.test(src);
80
+
81
+ await browser.close();
82
+
83
+ const restCount = restAll.length;
84
+ const restAllFalse = restAll.every(r => r.glow === 'false' && r.filter === 'none');
85
+ const pinnedTarget = pinnedAll?.find(r => r.glow === 'true');
86
+ const pinnedHasDropShadow = pinnedTarget && /drop-shadow/.test(pinnedTarget.filter);
87
+ const pinnedOthersStill = pinnedAll
88
+ ? pinnedAll.filter(r => r !== pinnedTarget).every(r => r.glow === 'false')
89
+ : false;
90
+
91
+ const results = {
92
+ rest_count_ge_3: restCount >= 3,
93
+ rest_all_glow_false: restAllFalse,
94
+ pinned_target_glow: pinnedTarget?.glow === 'true',
95
+ pinned_target_has_drop_shadow: pinnedHasDropShadow,
96
+ pinned_others_stay_rest: pinnedOthersStill,
97
+ source_glow_attr: sourceGlowAttr,
98
+ source_drop_shadow: sourceDropShadow,
99
+ source_filter_tween: sourceFilterTween,
100
+ };
101
+ const ok = Object.values(results).every(Boolean);
102
+ console.log(`${ok ? '✅' : '❌'} legend pin-ring drop-shadow glow:`, JSON.stringify(results),
103
+ '\n rest:', JSON.stringify(restAll),
104
+ '\n pinned target:', JSON.stringify(pinnedTarget));
105
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,93 @@
1
+ /* Round 475 verification: legend-row TEXT (row.label) transition
2
+ * cadence sync 150ms → 200ms. R473 lifted the legend-row TINT
3
+ * RECT but the text alongside still ran 150ms; R475 closes the
4
+ * legend-row scope internal desync mirroring R474 at recent-row.
5
+ *
6
+ * Contract:
7
+ * - every <text data-legend-row-label-transition='200ms'> renders
8
+ * (status rows: working / idle / offline / created / pending)
9
+ * - inline style includes 'fill 200ms ease-out' AND 'letter-
10
+ * spacing 200ms ease-out'
11
+ * - computed transitionDuration is '0.2s, 0.2s'
12
+ * - source-file conditional wired
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, status) => ({
35
+ alias, status, 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: [
40
+ mk('a·1', 'working'), mk('a·2', 'idle'), mk('a·3', 'offline'),
41
+ ] } });
42
+ });
43
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
44
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
45
+
46
+ const page = await ctx.newPage();
47
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
48
+ await page.waitForSelector('[data-legend-row-label-transition]', { timeout: 15000 });
49
+ await page.waitForTimeout(500);
50
+
51
+ const probe = await page.evaluate(() => {
52
+ const texts = [...document.querySelectorAll('[data-legend-row-label-transition]')];
53
+ return {
54
+ count: texts.length,
55
+ nodes: texts.map(t => {
56
+ const cs = getComputedStyle(t);
57
+ return {
58
+ attr: t.getAttribute('data-legend-row-label-transition'),
59
+ style: t.getAttribute('style') || '',
60
+ duration: cs.transitionDuration,
61
+ };
62
+ }),
63
+ };
64
+ });
65
+
66
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
67
+ const sourceAttr = /data-legend-row-label-transition="200ms"/.test(src);
68
+ const sourceStyle = /'fill 200ms ease-out, letter-spacing 200ms ease-out'/.test(src);
69
+
70
+ await browser.close();
71
+
72
+ const countGe2 = probe.count >= 2;
73
+ const allAttr200 = probe.nodes.every(n => n.attr === '200ms');
74
+ const allStyle200 = probe.nodes.every(n =>
75
+ /fill 200ms ease-out/.test(n.style) && /letter-spacing 200ms ease-out/.test(n.style));
76
+ const allDur200 = probe.nodes.every(n => /(^|, )0\.2s/.test(n.duration));
77
+ const noLegacy150 = probe.nodes.every(n =>
78
+ !/fill 150ms/.test(n.style) && !/letter-spacing 150ms/.test(n.style));
79
+
80
+ const results = {
81
+ text_count_ge_2: countGe2,
82
+ all_attr_200: allAttr200,
83
+ all_style_200: allStyle200,
84
+ all_computed_200: allDur200,
85
+ no_legacy_150_in_dom: noLegacy150,
86
+ source_attr: sourceAttr,
87
+ source_style: sourceStyle,
88
+ };
89
+ const ok = Object.values(results).every(Boolean);
90
+ console.log(`${ok ? '✅' : '❌'} legend-row text cadence 150→200:`, JSON.stringify(results),
91
+ '\n count:', probe.count,
92
+ '\n first:', JSON.stringify(probe.nodes[0]));
93
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,92 @@
1
+ /* Round 473 verification: legend-row tint rect transition cadence
2
+ * sync 150ms → 200ms. Final closure of the 3-tier panel-row cadence
3
+ * family — group-label (R459), recent-row (R472), legend-row (R473).
4
+ * All three label-class panel-row hitboxes now ease their fill +
5
+ * opacity at uniform 200ms ease-out.
6
+ *
7
+ * Contract:
8
+ * - every <rect data-legend-row-tint-transition='200ms'> renders
9
+ * (one per status legend row — 5 rows: working/idle/offline/+)
10
+ * - inline style is 'fill 200ms ease-out, opacity 200ms ease-out'
11
+ * - computed transition-duration is '0.2s, 0.2s'
12
+ * - NO 'fill 150ms' substring anywhere in any legend-row tint
13
+ * - source-file attr + style both wired
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
+ const browser = await chromium.launch({ headless: true });
22
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
23
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
24
+ await ctx.addInitScript(() => {
25
+ try {
26
+ localStorage.setItem('anet-theme', 'cyber');
27
+ localStorage.setItem('anet-topo-layout', 'ring');
28
+ sessionStorage.setItem('anet_v3_auth', '1');
29
+ } catch {}
30
+ });
31
+ await ctx.route('**/api/hub/status*', async (route) => {
32
+ const r = await route.fetch();
33
+ const b = await r.json();
34
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
35
+ const mk = (alias, status) => ({
36
+ alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
37
+ network_id: nid, project_dir: null,
38
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
39
+ });
40
+ await route.fulfill({ response: r, json: { ...b, sessions: [
41
+ mk('a·1', 'working'), mk('a·2', 'idle'), mk('a·3', 'offline'),
42
+ ] } });
43
+ });
44
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
45
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
46
+
47
+ const page = await ctx.newPage();
48
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
49
+ await page.waitForSelector('[data-legend-row-tint-transition]', { timeout: 15000 });
50
+ await page.waitForTimeout(500);
51
+
52
+ const probe = await page.evaluate(() => {
53
+ const rects = [...document.querySelectorAll('[data-legend-row-tint-transition]')];
54
+ return {
55
+ count: rects.length,
56
+ nodes: rects.map(r => {
57
+ const cs = getComputedStyle(r);
58
+ return {
59
+ attr: r.getAttribute('data-legend-row-tint-transition'),
60
+ style: r.getAttribute('style') || '',
61
+ duration: cs.transitionDuration,
62
+ };
63
+ }),
64
+ };
65
+ });
66
+
67
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
68
+ const sourceAttr = /data-legend-row-tint-transition="200ms"/.test(src);
69
+ const source150Removed = !src.includes('fill 150ms ease-out, opacity 150ms ease-out');
70
+
71
+ await browser.close();
72
+
73
+ const countGe2 = probe.count >= 2;
74
+ const allAttr200 = probe.nodes.every(n => n.attr === '200ms');
75
+ const allStyle200 = probe.nodes.every(n => /fill 200ms ease-out, opacity 200ms ease-out/.test(n.style));
76
+ const allDur200 = probe.nodes.every(n => /(^|, )0\.2s/.test(n.duration));
77
+ const noLegacy150 = probe.nodes.every(n => !/fill 150ms/.test(n.style));
78
+
79
+ const results = {
80
+ legend_rect_count_ge_2: countGe2,
81
+ all_attr_200: allAttr200,
82
+ all_style_200: allStyle200,
83
+ all_computed_200: allDur200,
84
+ no_legacy_150_in_dom: noLegacy150,
85
+ source_attr: sourceAttr,
86
+ source_no_150_anywhere: source150Removed,
87
+ };
88
+ const ok = Object.values(results).every(Boolean);
89
+ console.log(`${ok ? '✅' : '❌'} legend-row tint cadence 150→200:`, JSON.stringify(results),
90
+ '\n count:', probe.count,
91
+ '\n first:', JSON.stringify(probe.nodes[0]));
92
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,100 @@
1
+ /* Round 478 verification: recent-row freshness pip gains filter:
2
+ * drop-shadow glow when alpha > 0.7 (just-fired flow within ~30s).
3
+ * Freshness-gated (not pin/hover-gated), so the glow reads as
4
+ * "this signal is live" — natural breathing feel that tracks
5
+ * actual data freshness.
6
+ *
7
+ * Contract:
8
+ * - fresh (created_at = now): alpha=1.0, data-recent-row-freshness-
9
+ * glow='true', computed filter starts with 'drop-shadow'
10
+ * - stale (created_at = 5min ago): alpha→0.30, glow='false',
11
+ * filter === 'none'
12
+ * - source-file conditional wired
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 nowIso = () => new Date().toISOString();
19
+ const sessionFresh = new Date(Date.now() - 60 * 1000).toISOString();
20
+
21
+ const browser = await chromium.launch({ headless: true });
22
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
23
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
24
+ await ctx.addInitScript(() => {
25
+ try {
26
+ localStorage.setItem('anet-theme', 'cyber');
27
+ localStorage.setItem('anet-topo-layout', 'ring');
28
+ sessionStorage.setItem('anet_v3_auth', '1');
29
+ } catch {}
30
+ });
31
+ await ctx.route('**/api/hub/status*', async (route) => {
32
+ const r = await route.fetch();
33
+ const b = await r.json();
34
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
35
+ const mk = (alias, status) => ({
36
+ alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
37
+ network_id: nid, project_dir: null,
38
+ created_at: sessionFresh, updated_at: sessionFresh, last_seen_at: sessionFresh,
39
+ });
40
+ await route.fulfill({ response: r, json: { ...b, sessions: [
41
+ mk('a·1', 'working'), mk('a·2', 'idle'), mk('b·1', 'working'),
42
+ ] } });
43
+ });
44
+ // Mix fresh + stale messages so we get rows in both freshness tiers.
45
+ // Fresh: created NOW → alpha = 1.0 (within 30s gate)
46
+ // Stale: created 5min ago → alpha = 0.30 (R10 stale floor)
47
+ const fiveMinAgo = new Date(Date.now() - 5 * 60 * 1000).toISOString();
48
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: {
49
+ messages: [
50
+ { id: 'm1', from_alias: 'a·1', to_alias: 'a·2', content: 'live', created_at: nowIso() },
51
+ { id: 'm2', from_alias: 'b·1', to_alias: 'a·1', content: 'stale', created_at: fiveMinAgo },
52
+ ],
53
+ } }));
54
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
55
+
56
+ const page = await ctx.newPage();
57
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
58
+ await page.waitForSelector('[data-recent-row-freshness]', { timeout: 15000 });
59
+ await page.waitForTimeout(800);
60
+
61
+ const probe = await page.evaluate(() => {
62
+ const pips = [...document.querySelectorAll('[data-recent-row-freshness]')];
63
+ return pips.map(p => {
64
+ const cs = getComputedStyle(p);
65
+ return {
66
+ key: p.getAttribute('data-recent-row-freshness'),
67
+ alpha: parseFloat(p.getAttribute('data-recent-row-freshness-alpha') || '0'),
68
+ glow: p.getAttribute('data-recent-row-freshness-glow'),
69
+ filter: cs.filter,
70
+ };
71
+ });
72
+ });
73
+
74
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
75
+ const sourceGlowAttr = /data-recent-row-freshness-glow=\{alpha > 0\.7/.test(src);
76
+ const sourceDropShadow = /drop-shadow\(0 0 3px \$\{pal\.legendAccent\}80\)/.test(src);
77
+ const sourceFilterTween = /filter 200ms ease-out/.test(src);
78
+
79
+ await browser.close();
80
+
81
+ const fresh = probe.find(p => p.alpha > 0.7);
82
+ const stale = probe.find(p => p.alpha < 0.5);
83
+
84
+ const results = {
85
+ pips_count_ge_2: probe.length >= 2,
86
+ fresh_pip_found: !!fresh,
87
+ fresh_glow_true: fresh?.glow === 'true',
88
+ fresh_filter_has_drop: fresh && /drop-shadow/.test(fresh.filter),
89
+ stale_pip_found: !!stale,
90
+ stale_glow_false: stale?.glow === 'false',
91
+ stale_filter_none: stale?.filter === 'none',
92
+ source_glow_attr: sourceGlowAttr,
93
+ source_drop_shadow: sourceDropShadow,
94
+ source_filter_tween: sourceFilterTween,
95
+ };
96
+ const ok = Object.values(results).every(Boolean);
97
+ console.log(`${ok ? '✅' : '❌'} recent-row freshness pip drop-shadow:`, JSON.stringify(results),
98
+ '\n fresh pip:', JSON.stringify(fresh),
99
+ '\n stale pip:', JSON.stringify(stale));
100
+ process.exit(ok ? 0 : 1);