@sleep2agi/agent-network-dashboard 0.5.1-preview.97 → 0.5.1-preview.99
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.
- package/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +3 -3
- package/.next/diagnostics/route-bundle-stats.json +4 -4
- package/.next/fallback-build-manifest.json +3 -3
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +1 -1
- package/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/admin.html +1 -1
- package/.next/server/app/admin.rsc +1 -1
- package/.next/server/app/admin.segments/_full.segment.rsc +1 -1
- package/.next/server/app/admin.segments/_head.segment.rsc +1 -1
- package/.next/server/app/admin.segments/_index.segment.rsc +1 -1
- package/.next/server/app/admin.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/admin.segments/admin.segment.rsc +1 -1
- package/.next/server/app/index.html +2 -2
- package/.next/server/app/index.rsc +2 -2
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/index.segments/_full.segment.rsc +2 -2
- package/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/login.html +2 -2
- package/.next/server/app/login.rsc +2 -2
- package/.next/server/app/login.segments/_full.segment.rsc +2 -2
- package/.next/server/app/login.segments/_head.segment.rsc +1 -1
- package/.next/server/app/login.segments/_index.segment.rsc +1 -1
- package/.next/server/app/login.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/login.segments/login.segment.rsc +1 -1
- package/.next/server/app/logs.html +1 -1
- package/.next/server/app/logs.rsc +1 -1
- package/.next/server/app/logs.segments/_full.segment.rsc +1 -1
- package/.next/server/app/logs.segments/_head.segment.rsc +1 -1
- package/.next/server/app/logs.segments/_index.segment.rsc +1 -1
- package/.next/server/app/logs.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/logs.segments/logs.segment.rsc +1 -1
- package/.next/server/app/messages.html +1 -1
- package/.next/server/app/messages.rsc +1 -1
- package/.next/server/app/messages.segments/_full.segment.rsc +1 -1
- package/.next/server/app/messages.segments/_head.segment.rsc +1 -1
- package/.next/server/app/messages.segments/_index.segment.rsc +1 -1
- package/.next/server/app/messages.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/messages.segments/messages.segment.rsc +1 -1
- package/.next/server/app/node.html +1 -1
- package/.next/server/app/node.rsc +1 -1
- package/.next/server/app/node.segments/_full.segment.rsc +1 -1
- package/.next/server/app/node.segments/_head.segment.rsc +1 -1
- package/.next/server/app/node.segments/_index.segment.rsc +1 -1
- package/.next/server/app/node.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/node.segments/node.segment.rsc +1 -1
- package/.next/server/app/nodes.html +1 -1
- package/.next/server/app/nodes.rsc +1 -1
- package/.next/server/app/nodes.segments/_full.segment.rsc +1 -1
- package/.next/server/app/nodes.segments/_head.segment.rsc +1 -1
- package/.next/server/app/nodes.segments/_index.segment.rsc +1 -1
- package/.next/server/app/nodes.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/nodes.segments/nodes.segment.rsc +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/server-logs.html +1 -1
- package/.next/server/app/server-logs.rsc +1 -1
- package/.next/server/app/server-logs.segments/_full.segment.rsc +1 -1
- package/.next/server/app/server-logs.segments/_head.segment.rsc +1 -1
- package/.next/server/app/server-logs.segments/_index.segment.rsc +1 -1
- package/.next/server/app/server-logs.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/server-logs.segments/server-logs.segment.rsc +1 -1
- package/.next/server/app/settings/networks.html +1 -1
- package/.next/server/app/settings/networks.rsc +1 -1
- package/.next/server/app/settings/networks.segments/_full.segment.rsc +1 -1
- package/.next/server/app/settings/networks.segments/_head.segment.rsc +1 -1
- package/.next/server/app/settings/networks.segments/_index.segment.rsc +1 -1
- package/.next/server/app/settings/networks.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +1 -1
- package/.next/server/app/settings/networks.segments/settings.segment.rsc +1 -1
- package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens.html +1 -1
- package/.next/server/app/settings/tokens.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/_full.segment.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/_head.segment.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/_index.segment.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/settings.segment.rsc +1 -1
- package/.next/server/app/settings.html +2 -2
- package/.next/server/app/settings.rsc +2 -2
- package/.next/server/app/settings.segments/_full.segment.rsc +2 -2
- package/.next/server/app/settings.segments/_head.segment.rsc +1 -1
- package/.next/server/app/settings.segments/_index.segment.rsc +1 -1
- package/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/settings.segments/settings.segment.rsc +1 -1
- package/.next/server/app/tasks.html +1 -1
- package/.next/server/app/tasks.rsc +1 -1
- package/.next/server/app/tasks.segments/_full.segment.rsc +1 -1
- package/.next/server/app/tasks.segments/_head.segment.rsc +1 -1
- package/.next/server/app/tasks.segments/_index.segment.rsc +1 -1
- package/.next/server/app/tasks.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/tasks.segments/tasks.segment.rsc +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
- package/.next/server/middleware-build-manifest.js +3 -3
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/static/chunks/{085uyvy61dd-4.js → 0.sf46gnv4wwm.js} +1 -1
- package/.next/static/chunks/{0a1v54cnxx83..js → 0_igeywsok2_-.js} +1 -1
- package/.next/static/chunks/0igz0bww16uvc.js +4 -0
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/app/components/TopoGraph.tsx +77 -16
- package/package.json +1 -1
- package/scripts/topo-flow-rail-hover-sw-test.mjs +113 -0
- package/scripts/topo-status-ring-hover-sw-test.mjs +105 -0
- package/.next/static/chunks/0ap~mvvqp~v-_.js +0 -4
- /package/.next/static/{44j_WBni902eK_p3ToCrp → 4rHxTzLwe2XC9M1rN-MpJ}/_buildManifest.js +0 -0
- /package/.next/static/{44j_WBni902eK_p3ToCrp → 4rHxTzLwe2XC9M1rN-MpJ}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{44j_WBni902eK_p3ToCrp → 4rHxTzLwe2XC9M1rN-MpJ}/_ssgManifest.js +0 -0
|
@@ -4921,13 +4921,32 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
4921
4921
|
d={path}
|
|
4922
4922
|
fill="none"
|
|
4923
4923
|
stroke={pal.flowPath}
|
|
4924
|
-
strokeWidth
|
|
4924
|
+
/* Round 437 / Loop: flow-rail strokeWidth hover lift —
|
|
4925
|
+
1 → 1.5 on (isHoveredEdge || isEndpointHoveredEdge).
|
|
4926
|
+
Pre-R437 the dashed rail sat at sw=1 always while the
|
|
4927
|
+
visible flow path above it lifted (R50 ×1.4 on
|
|
4928
|
+
isHoveredEdge / R436 ×1.15 on isEndpointHoveredEdge).
|
|
4929
|
+
The two edge paint layers were mismatched on hover:
|
|
4930
|
+
top layer thickened, underline stayed thin — so the
|
|
4931
|
+
hover gesture lifted only half the edge surface.
|
|
4932
|
+
R437 lifts the underline too so the whole edge
|
|
4933
|
+
reads as "raised" on hover, not just its bright
|
|
4934
|
+
top stripe. Same +0.5 absolute delta R435 used at
|
|
4935
|
+
hub-spoke scope (1→1.25 there, slightly bigger
|
|
4936
|
+
here because the rail's base 1 is at the kerning
|
|
4937
|
+
floor and needs more lift to register).
|
|
4938
|
+
Transition list extends to include stroke-width
|
|
4939
|
+
300ms so the new lift eases under the same R166
|
|
4940
|
+
cadence as the visible path's stroke-width. */
|
|
4941
|
+
strokeWidth={(isHoveredEdge || isEndpointHoveredEdge) ? 1.5 : 1}
|
|
4925
4942
|
strokeDasharray="2 12"
|
|
4926
4943
|
strokeLinecap="round"
|
|
4927
4944
|
opacity={Math.min(1, (isLight ? 0.4 : 0.75) * fresh * edgeOpacityMul)}
|
|
4928
4945
|
data-edge-flow-rail={link.key}
|
|
4929
4946
|
data-edge-flow-rail-linecap="round"
|
|
4930
|
-
|
|
4947
|
+
data-edge-flow-rail-stroke-width={(isHoveredEdge || isEndpointHoveredEdge) ? 1.5 : 1}
|
|
4948
|
+
data-edge-flow-rail-lifted={(isHoveredEdge || isEndpointHoveredEdge) ? 'true' : 'false'}
|
|
4949
|
+
style={{ transition: 'opacity 300ms ease-out, stroke 300ms ease-out, stroke-width 300ms ease-out' }}
|
|
4931
4950
|
/>
|
|
4932
4951
|
{!reducedMotion && (
|
|
4933
4952
|
/* Round 103 / Loop: phase-stagger the particles so
|
|
@@ -6709,20 +6728,62 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
6709
6728
|
stroke-width="3"/"1.5" still works against the
|
|
6710
6729
|
DOM attribute value (React-rendered, not
|
|
6711
6730
|
interpolated). */}
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
6719
|
-
|
|
6720
|
-
|
|
6721
|
-
|
|
6722
|
-
|
|
6723
|
-
|
|
6724
|
-
|
|
6725
|
-
|
|
6731
|
+
{(() => {
|
|
6732
|
+
/* Round 438 / Loop: status ring strokeWidth hover lift —
|
|
6733
|
+
when hoveredAlias matches, the node's status ring
|
|
6734
|
+
thickens by +0.5: online 3 → 3.5, offline 1.5 → 2.
|
|
6735
|
+
Same absolute delta as R435 hub-spoke (idle 1→1.25
|
|
6736
|
+
used Δ +0.25 because rest base was thinner; status
|
|
6737
|
+
ring's heavier rest values 1.5/3 need a bigger
|
|
6738
|
+
+0.5 to register as visible thickening).
|
|
6739
|
+
Status-ring axis joins the node-hover cue stack
|
|
6740
|
+
(now 9 layers including link surfaces):
|
|
6741
|
+
R26 group translateY -2px per-node geometry
|
|
6742
|
+
R217 stroke tint legendAccent per-node card
|
|
6743
|
+
R142 drop-shadow boost per-node card
|
|
6744
|
+
R427 alias letter-spacing per-node text
|
|
6745
|
+
R428 sub-text letter-spacing per-node text
|
|
6746
|
+
R429 body opacity 0.94 → 1.0 per-node card
|
|
6747
|
+
R430 hub-spoke α+ link to hub (paint)
|
|
6748
|
+
R435 hub-spoke sw+ link to hub (geo)
|
|
6749
|
+
R94 edge α 1.7× inter-node link (paint)
|
|
6750
|
+
R436 edge sw 1.15× inter-node link (geo)
|
|
6751
|
+
R437 flow-rail sw 1 → 1.5 edge paint-layer
|
|
6752
|
+
R438 status-ring sw +0.5 ring geometry ← this round
|
|
6753
|
+
R51 sentinel safety: rest values 3 / 1.5 unchanged
|
|
6754
|
+
so the overlap-test selector `circle[stroke-width=
|
|
6755
|
+
"3"]` / `circle[stroke-width="1.5"]` inside
|
|
6756
|
+
g[data-node] still matches at rest. Hover values
|
|
6757
|
+
3.5 / 2 are not in the reserved {1.5, 3} set so
|
|
6758
|
+
the selector wouldn't match them anyway; but the
|
|
6759
|
+
test runs WITHOUT hover so this never matters
|
|
6760
|
+
in practice. R167 stroke-width 300ms transition
|
|
6761
|
+
already in the style list eases the lift for
|
|
6762
|
+
free. data-node-status-ring-hovered exposes the
|
|
6763
|
+
gate for tests. */
|
|
6764
|
+
const isRingHovered = !reducedMotion && hoveredAlias === session.alias;
|
|
6765
|
+
const ringStrokeWidth = isOnline
|
|
6766
|
+
? (isRingHovered ? 3.5 : 3)
|
|
6767
|
+
: (isRingHovered ? 2 : 1.5);
|
|
6768
|
+
return (
|
|
6769
|
+
<circle
|
|
6770
|
+
cx={pos.x}
|
|
6771
|
+
cy={pos.y}
|
|
6772
|
+
r={radius}
|
|
6773
|
+
fill={isOnline ? pal.nodeFill.online : pal.nodeFill.offline}
|
|
6774
|
+
stroke={status.primary}
|
|
6775
|
+
strokeWidth={ringStrokeWidth}
|
|
6776
|
+
strokeDasharray={isOnline ? 'none' : '5 5'}
|
|
6777
|
+
filter={isOnline && !isLight ? 'url(#topo-glow)' : undefined}
|
|
6778
|
+
data-node-status-ring={status.label}
|
|
6779
|
+
data-node-status-ring-hovered={isRingHovered ? 'true' : 'false'}
|
|
6780
|
+
data-node-status-ring-stroke-width={ringStrokeWidth}
|
|
6781
|
+
style={{
|
|
6782
|
+
transition: 'fill 300ms ease-out, stroke 300ms ease-out, stroke-width 300ms ease-out',
|
|
6783
|
+
}}
|
|
6784
|
+
/>
|
|
6785
|
+
);
|
|
6786
|
+
})()}
|
|
6726
6787
|
{/* v0.10.0 Hero 1+2 / §3.F server-health node-ring tint.
|
|
6727
6788
|
When the host server this agent runs on is in the
|
|
6728
6789
|
`red` tier (CPU/Mem/Disk worst-of ≥ 85% per
|
package/package.json
CHANGED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/* Round 437 verification: flow-rail (dashed underline) strokeWidth
|
|
2
|
+
* hover lift — 1 → 1.5 on (isHoveredEdge || isEndpointHoveredEdge).
|
|
3
|
+
* Pairs with R50 visible-path width lift + R436 endpoint width lift
|
|
4
|
+
* so both edge paint layers thicken together on hover.
|
|
5
|
+
*
|
|
6
|
+
* Contract:
|
|
7
|
+
* - rest: every flow-rail reports stroke-width '1' + lifted='false'
|
|
8
|
+
* - hover one node alias: rails on edges incident to that node
|
|
9
|
+
* report stroke-width '1.5' + lifted='true'; non-incident rails
|
|
10
|
+
* stay rest
|
|
11
|
+
* - source-file probe confirms conditional + transition extension
|
|
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: 1500 } });
|
|
21
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
22
|
+
await ctx.addInitScript(() => {
|
|
23
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } 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, status) => ({
|
|
30
|
+
alias, status, 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: [
|
|
35
|
+
mk('alpha', 'working'),
|
|
36
|
+
mk('beta', 'idle'),
|
|
37
|
+
mk('gamma', 'idle'),
|
|
38
|
+
] } });
|
|
39
|
+
});
|
|
40
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({
|
|
41
|
+
json: { messages: [
|
|
42
|
+
{ id: 'm1', from_alias: 'alpha', to_alias: 'beta', content: 'ping', created_at: fresh, network_id: 'default' },
|
|
43
|
+
{ id: 'm2', from_alias: 'alpha', to_alias: 'gamma', content: 'pong', created_at: fresh, network_id: 'default' },
|
|
44
|
+
{ id: 'm3', from_alias: 'gamma', to_alias: 'beta', content: 'pang', created_at: fresh, network_id: 'default' },
|
|
45
|
+
] },
|
|
46
|
+
}));
|
|
47
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
48
|
+
|
|
49
|
+
const page = await ctx.newPage();
|
|
50
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
51
|
+
await page.waitForSelector('[data-edge-flow-rail]', { timeout: 15000 });
|
|
52
|
+
await page.waitForTimeout(400);
|
|
53
|
+
|
|
54
|
+
const readAll = () => page.evaluate(() => {
|
|
55
|
+
const ps = [...document.querySelectorAll('[data-edge-flow-rail]')];
|
|
56
|
+
return ps.map(p => ({
|
|
57
|
+
key: p.getAttribute('data-edge-flow-rail'),
|
|
58
|
+
sw: parseFloat(p.getAttribute('data-edge-flow-rail-stroke-width') || '0'),
|
|
59
|
+
sw_a: p.getAttribute('stroke-width'),
|
|
60
|
+
lifted: p.getAttribute('data-edge-flow-rail-lifted'),
|
|
61
|
+
}));
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const rest = await readAll();
|
|
65
|
+
|
|
66
|
+
let hover = null;
|
|
67
|
+
const box = await page.evaluate(() => {
|
|
68
|
+
const t = document.querySelector('[data-node-alias-text="alpha"]');
|
|
69
|
+
if (!t) return null;
|
|
70
|
+
const node = t.closest('[data-node]');
|
|
71
|
+
const target = node || t;
|
|
72
|
+
const b = target.getBoundingClientRect();
|
|
73
|
+
return { x: b.x + b.width / 2, y: b.y + b.height / 2 };
|
|
74
|
+
});
|
|
75
|
+
if (box) {
|
|
76
|
+
await page.mouse.move(box.x, box.y);
|
|
77
|
+
await page.waitForTimeout(300);
|
|
78
|
+
hover = await readAll();
|
|
79
|
+
await page.mouse.move(0, 0);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
|
|
83
|
+
const sourceWired = /strokeWidth=\{\(isHoveredEdge \|\| isEndpointHoveredEdge\) \? 1\.5 : 1\}/.test(fileText);
|
|
84
|
+
const sourceTransition = /transition: 'opacity 300ms ease-out, stroke 300ms ease-out, stroke-width 300ms ease-out'/.test(fileText);
|
|
85
|
+
|
|
86
|
+
await browser.close();
|
|
87
|
+
|
|
88
|
+
const restAllSw1 = rest.every(r => r.sw === 1);
|
|
89
|
+
const restNoneLifted = rest.every(r => r.lifted === 'false');
|
|
90
|
+
|
|
91
|
+
const isAlphaKey = (k) => /alpha/i.test(k);
|
|
92
|
+
const hoverByKey = new Map((hover || []).map(h => [h.key, h]));
|
|
93
|
+
const alphaEntries = [...hoverByKey.entries()].filter(([k]) => isAlphaKey(k));
|
|
94
|
+
const nonAlphaEntries = [...hoverByKey.entries()].filter(([k]) => !isAlphaKey(k));
|
|
95
|
+
|
|
96
|
+
const alphaAllLifted = alphaEntries.length > 0 && alphaEntries.every(([, h]) => h.sw === 1.5 && h.lifted === 'true');
|
|
97
|
+
const nonAlphaRestSw = nonAlphaEntries.every(([, h]) => h.sw === 1 && h.lifted === 'false');
|
|
98
|
+
|
|
99
|
+
const results = {
|
|
100
|
+
rest_count_ge_2: rest.length >= 2,
|
|
101
|
+
rest_all_sw_1: restAllSw1,
|
|
102
|
+
rest_none_lifted: restNoneLifted,
|
|
103
|
+
hover_alpha_count_ge_2: alphaEntries.length >= 2,
|
|
104
|
+
hover_alpha_all_lifted: alphaAllLifted,
|
|
105
|
+
hover_non_alpha_unaffected: nonAlphaRestSw,
|
|
106
|
+
source_strokeWidth_wired: sourceWired,
|
|
107
|
+
source_transition_wired: sourceTransition,
|
|
108
|
+
};
|
|
109
|
+
const ok = Object.values(results).every(Boolean);
|
|
110
|
+
console.log(`${ok ? '✅' : '❌'} flow-rail hover sw lift:`, JSON.stringify(results),
|
|
111
|
+
'\n rest:', JSON.stringify(rest),
|
|
112
|
+
'\n hover:', JSON.stringify(hover));
|
|
113
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/* Round 438 verification: status ring strokeWidth hover lift —
|
|
2
|
+
* online 3 → 3.5, offline 1.5 → 2 on hoveredAlias match.
|
|
3
|
+
*
|
|
4
|
+
* Contract:
|
|
5
|
+
* - rest: every online ring reports stroke-width '3', every
|
|
6
|
+
* offline ring '1.5' (R51 sentinels preserved at rest)
|
|
7
|
+
* - hover one node: that ring's stroke-width lifts to '3.5' (or '2'
|
|
8
|
+
* if offline) and data-node-status-ring-hovered flips to 'true'
|
|
9
|
+
* - siblings stay rest
|
|
10
|
+
* - R51 sentinel ATTRS unchanged on rest (overlap probe still works)
|
|
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: 1500 } });
|
|
20
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
21
|
+
await ctx.addInitScript(() => {
|
|
22
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
23
|
+
});
|
|
24
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
25
|
+
const r = await route.fetch();
|
|
26
|
+
const b = await r.json();
|
|
27
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
28
|
+
const mk = (alias, status) => ({
|
|
29
|
+
alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
30
|
+
network_id: nid, project_dir: null,
|
|
31
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
32
|
+
});
|
|
33
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
34
|
+
mk('alpha', 'working'), // online → sw 3
|
|
35
|
+
mk('beta', 'idle'), // online → sw 3
|
|
36
|
+
mk('gamma', 'working'), // online → sw 3
|
|
37
|
+
] } });
|
|
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
|
+
|
|
42
|
+
const page = await ctx.newPage();
|
|
43
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
44
|
+
await page.waitForSelector('[data-node-status-ring]', { timeout: 15000 });
|
|
45
|
+
await page.waitForTimeout(400);
|
|
46
|
+
|
|
47
|
+
const readAll = () => page.evaluate(() => {
|
|
48
|
+
const cs = [...document.querySelectorAll('[data-node-status-ring]')];
|
|
49
|
+
return cs.map((c, i) => {
|
|
50
|
+
const node = c.closest('[data-node]');
|
|
51
|
+
return {
|
|
52
|
+
idx: i,
|
|
53
|
+
alias: node?.getAttribute('data-node') || null,
|
|
54
|
+
ring: c.getAttribute('data-node-status-ring'),
|
|
55
|
+
sw: c.getAttribute('stroke-width'),
|
|
56
|
+
hovered: c.getAttribute('data-node-status-ring-hovered'),
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const rest = await readAll();
|
|
62
|
+
|
|
63
|
+
const firstAlias = rest[0]?.alias || 'alpha';
|
|
64
|
+
let hover = null;
|
|
65
|
+
const box = await page.evaluate((alias) => {
|
|
66
|
+
const node = document.querySelector(`[data-node="${alias}"]`);
|
|
67
|
+
if (!node) return null;
|
|
68
|
+
const b = node.getBoundingClientRect();
|
|
69
|
+
return { x: b.x + b.width / 2, y: b.y + b.height / 2 };
|
|
70
|
+
}, firstAlias);
|
|
71
|
+
if (box) {
|
|
72
|
+
await page.mouse.move(box.x, box.y);
|
|
73
|
+
await page.waitForTimeout(300);
|
|
74
|
+
hover = await readAll();
|
|
75
|
+
await page.mouse.move(0, 0);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
|
|
79
|
+
const sourceWired = /const ringStrokeWidth = isOnline\s*\n\s*\?\s*\(isRingHovered \? 3\.5 : 3\)\s*\n\s*:\s*\(isRingHovered \? 2 : 1\.5\)/.test(fileText);
|
|
80
|
+
const sourceIsHovered = /const isRingHovered = !reducedMotion && hoveredAlias === session\.alias/.test(fileText);
|
|
81
|
+
|
|
82
|
+
await browser.close();
|
|
83
|
+
|
|
84
|
+
const restAllSw3 = rest.every(r => r.sw === '3'); // working+idle both online
|
|
85
|
+
const restNoHover = rest.every(r => r.hovered === 'false');
|
|
86
|
+
const hoveredEntry = hover?.find(r => r.alias === firstAlias);
|
|
87
|
+
const hoverSw_3_5 = hoveredEntry?.sw === '3.5';
|
|
88
|
+
const hoverHoveredTrue = hoveredEntry?.hovered === 'true';
|
|
89
|
+
const othersStillSw3 = hover ? hover.filter(r => r.alias !== firstAlias).every(r => r.sw === '3' && r.hovered === 'false') : false;
|
|
90
|
+
|
|
91
|
+
const results = {
|
|
92
|
+
rest_ring_count_ge_3: rest.length >= 3,
|
|
93
|
+
rest_all_online_sw_3: restAllSw3,
|
|
94
|
+
rest_no_hover: restNoHover,
|
|
95
|
+
hover_target_sw_3_5: hoverSw_3_5,
|
|
96
|
+
hover_target_attr_true: hoverHoveredTrue,
|
|
97
|
+
hover_others_stay_sw_3: othersStillSw3,
|
|
98
|
+
source_strokeWidth_wired: sourceWired,
|
|
99
|
+
source_isRingHovered_def: sourceIsHovered,
|
|
100
|
+
};
|
|
101
|
+
const ok = Object.values(results).every(Boolean);
|
|
102
|
+
console.log(`${ok ? '✅' : '❌'} status-ring hover sw:`, JSON.stringify(results),
|
|
103
|
+
'\n rest sample:', JSON.stringify(rest[0]),
|
|
104
|
+
'\n hover target:', JSON.stringify(hoveredEntry));
|
|
105
|
+
process.exit(ok ? 0 : 1);
|