@sleep2agi/agent-network-dashboard 0.5.3-preview.3 → 0.5.3-preview.30
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 +32 -32
- 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/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +2 -2
- package/.next/server/app/_not-found.rsc +12 -12
- package/.next/server/app/_not-found.segments/_full.segment.rsc +12 -12
- package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/server/app/_not-found.segments/_index.segment.rsc +7 -7
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
- package/.next/server/app/admin.html +2 -2
- package/.next/server/app/admin.rsc +14 -14
- package/.next/server/app/admin.segments/_full.segment.rsc +14 -14
- package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
- package/.next/server/app/admin.segments/_index.segment.rsc +7 -7
- package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
- package/.next/server/app/index.html +2 -2
- package/.next/server/app/index.rsc +14 -14
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/index.segments/_full.segment.rsc +14 -14
- package/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/server/app/index.segments/_index.segment.rsc +7 -7
- package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- 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 +14 -14
- package/.next/server/app/login.segments/_full.segment.rsc +14 -14
- package/.next/server/app/login.segments/_head.segment.rsc +4 -4
- package/.next/server/app/login.segments/_index.segment.rsc +7 -7
- package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/login.segments/login.segment.rsc +3 -3
- package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/logs.html +2 -2
- package/.next/server/app/logs.rsc +14 -14
- package/.next/server/app/logs.segments/_full.segment.rsc +14 -14
- package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/logs.segments/_index.segment.rsc +7 -7
- package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
- package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
- package/.next/server/app/messages.html +2 -2
- package/.next/server/app/messages.rsc +14 -14
- package/.next/server/app/messages.segments/_full.segment.rsc +14 -14
- package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
- package/.next/server/app/messages.segments/_index.segment.rsc +7 -7
- package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
- package/.next/server/app/node/page_client-reference-manifest.js +1 -1
- package/.next/server/app/node.html +2 -2
- package/.next/server/app/node.rsc +14 -14
- package/.next/server/app/node.segments/_full.segment.rsc +14 -14
- package/.next/server/app/node.segments/_head.segment.rsc +4 -4
- package/.next/server/app/node.segments/_index.segment.rsc +7 -7
- package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/node.segments/node.segment.rsc +3 -3
- package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
- package/.next/server/app/nodes.html +2 -2
- package/.next/server/app/nodes.rsc +14 -14
- package/.next/server/app/nodes.segments/_full.segment.rsc +14 -14
- package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
- package/.next/server/app/nodes.segments/_index.segment.rsc +7 -7
- package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/server-logs.html +2 -2
- package/.next/server/app/server-logs.rsc +14 -14
- package/.next/server/app/server-logs.segments/_full.segment.rsc +14 -14
- package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/server-logs.segments/_index.segment.rsc +7 -7
- package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
- package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/networks.html +2 -2
- package/.next/server/app/settings/networks.rsc +14 -14
- package/.next/server/app/settings/networks.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/networks.segments/_index.segment.rsc +7 -7
- package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
- package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
- package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens.html +2 -2
- package/.next/server/app/settings/tokens.rsc +14 -14
- package/.next/server/app/settings/tokens.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/tokens.segments/_index.segment.rsc +7 -7
- package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
- package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
- package/.next/server/app/settings.html +2 -2
- package/.next/server/app/settings.rsc +14 -14
- package/.next/server/app/settings.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings.segments/_index.segment.rsc +7 -7
- package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
- package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
- package/.next/server/app/tasks.html +2 -2
- package/.next/server/app/tasks.rsc +14 -14
- package/.next/server/app/tasks.segments/_full.segment.rsc +14 -14
- package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/tasks.segments/_index.segment.rsc +7 -7
- package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +3 -3
- 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/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js.map +1 -1
- package/.next/server/middleware-build-manifest.js +3 -3
- package/.next/server/pages/404.html +2 -2
- package/.next/server/pages/500.html +1 -1
- package/.next/static/chunks/02.trkmkuwcuu.js +4 -0
- package/.next/static/chunks/0d.g4hk1qlsa3.js +1 -0
- package/.next/static/chunks/{0kurrvjgmhct8.js → 0g3amqqhiubl2.js} +1 -1
- package/.next/static/chunks/0m.1mvl~t.avc.css +2 -0
- package/.next/static/chunks/0n5a06bgte2qp.js +1 -0
- package/.next/static/chunks/{03a4--7ncekmk.js → 0v4-5tng.uh.7.js} +2 -2
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/app/components/ServersDrawer.tsx +16 -3
- package/app/components/TopoGraph.tsx +652 -40
- package/app/globals.css +51 -3
- package/package.json +4 -4
- package/scripts/p157-servers-copy-test.mjs +95 -0
- package/scripts/topo-alias-glow-test.mjs +121 -0
- package/scripts/topo-avatar-brightness-test.mjs +116 -0
- package/scripts/topo-chip-row-press-test.mjs +93 -0
- package/scripts/topo-chrome-press-fullstrip-test.mjs +105 -0
- package/scripts/topo-chrome-press-scale-test.mjs +100 -0
- package/scripts/topo-cluster-count-attr-test.mjs +80 -0
- package/scripts/topo-filter-pills-press-test.mjs +96 -0
- package/scripts/topo-fleet-density-tier-test.mjs +84 -0
- package/scripts/topo-freshness-chip-fade-test.mjs +105 -0
- package/scripts/topo-fullscreen-attr-test.mjs +73 -0
- package/scripts/topo-grid-content-bottom-attr-test.mjs +72 -0
- package/scripts/topo-hub-highlight-amplify-test.mjs +139 -0
- package/scripts/topo-hub-highlight-fill-transition-test.mjs +84 -0
- package/scripts/topo-hub-highlight-recede-test.mjs +144 -0
- package/scripts/topo-hub-highlight-theme-fill-test.mjs +83 -0
- package/scripts/topo-hub-idle-breath-test.mjs +109 -0
- package/scripts/topo-hub-recede-test.mjs +124 -0
- package/scripts/topo-orphan-box-dash-test.mjs +89 -0
- package/scripts/topo-orphan-fill-opacity-test.mjs +91 -0
- package/scripts/topo-orphan-label-italic-test.mjs +90 -0
- package/scripts/topo-pinned-aspect-test.mjs +89 -0
- package/scripts/topo-recent-hot-pulse-test.mjs +102 -0
- package/scripts/topo-reduced-motion-attr-test.mjs +69 -0
- package/scripts/topo-reset-icon-hover-scale-test.mjs +102 -0
- package/scripts/topo-svg-focus-transition-test.mjs +105 -0
- package/scripts/topo-vendor-activelinks-press-test.mjs +100 -0
- package/.next/static/chunks/0a~3lmgl2.3sm.js +0 -4
- package/.next/static/chunks/0c5zf9f-h7gni.js +0 -1
- package/.next/static/chunks/0i2qt3ct3dh73.js +0 -1
- package/.next/static/chunks/0x.63iu4he92k.css +0 -2
- /package/.next/static/{9MPaBwgdo_VS82WDvA3Qb → 3GzOANlRfMMbjx-RdP7Jh}/_buildManifest.js +0 -0
- /package/.next/static/{9MPaBwgdo_VS82WDvA3Qb → 3GzOANlRfMMbjx-RdP7Jh}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{9MPaBwgdo_VS82WDvA3Qb → 3GzOANlRfMMbjx-RdP7Jh}/_ssgManifest.js +0 -0
package/app/globals.css
CHANGED
|
@@ -863,6 +863,29 @@ body {
|
|
|
863
863
|
transform-box: fill-box;
|
|
864
864
|
}
|
|
865
865
|
|
|
866
|
+
/* Round 498 — recent-signal row hot-count subtle pulse (信息密度 +
|
|
867
|
+
呼吸感 themes). When a row's edge count crosses the hot threshold
|
|
868
|
+
(≥ 10), the existing R127/R320/R445 amber-fill + fw-700 typography
|
|
869
|
+
already calls attention; R498 adds a slow 3s opacity breath (0.85 ↔
|
|
870
|
+
1.0) on the digit so the hot tspans gently pulse — at-a-glance scan
|
|
871
|
+
of the panel reads "high-traffic lane right here" with motion in
|
|
872
|
+
addition to color+weight. 3s cycle is deliberate: faster (1-2s)
|
|
873
|
+
would compete with the working-status halo cadence; slower (5s+)
|
|
874
|
+
would lose the "alive" signal. Amplitude 15% (matches R497 hub-
|
|
875
|
+
highlight breath idiom).
|
|
876
|
+
prefers-reduced-motion handled by the R29 blanket override
|
|
877
|
+
(animation-duration: 0.001ms !important) — no per-class guard
|
|
878
|
+
needed. The component-side gate (`!reducedMotion && isHot`)
|
|
879
|
+
ensures the className is only applied when both conditions hold,
|
|
880
|
+
so even without the blanket the no-motion preference is respected. */
|
|
881
|
+
@keyframes anet-recent-hot-pulse-kf {
|
|
882
|
+
0%, 100% { opacity: 0.85; }
|
|
883
|
+
50% { opacity: 1; }
|
|
884
|
+
}
|
|
885
|
+
.anet-recent-hot-pulse {
|
|
886
|
+
animation: anet-recent-hot-pulse-kf 3s ease-in-out infinite;
|
|
887
|
+
}
|
|
888
|
+
|
|
866
889
|
/* Round 36 — current-step ring pulse on TaskDrawer timeline. Halo gently
|
|
867
890
|
breathes around the active step's dot so users can spot "task is here". */
|
|
868
891
|
@keyframes anet-current-step-pulse-kf {
|
|
@@ -951,10 +974,35 @@ body {
|
|
|
951
974
|
R144/R151/R152). Browser default focus outline on SVG is hard
|
|
952
975
|
to spot against the canvas; explicit cyan-300 ring matches the
|
|
953
976
|
dashboard's legendAccent. 2-px outline-offset gives breathing
|
|
954
|
-
room around the painted bounding box.
|
|
955
|
-
|
|
956
|
-
|
|
977
|
+
room around the painted bounding box.
|
|
978
|
+
Round 491 / Loop — SVG-side counterpart to R490's chip-focus
|
|
979
|
+
outline-color transition. Pre-R491 keyboard focus on SVG g
|
|
980
|
+
elements (recent rows, legend rows, group labels, edge badges,
|
|
981
|
+
nodes, "+N more") snapped instantly; HTML chips (post-R490) now
|
|
982
|
+
ease through 200ms ease-out but SVG g still hard-cut. Same
|
|
983
|
+
baseline-transparent + transition-outline-color recipe brings
|
|
984
|
+
the SVG canvas into the unified keyboard motion vocabulary —
|
|
985
|
+
keyboard users tabbing between chip-row and SVG canvas see
|
|
986
|
+
ONE smooth fade timing instead of HTML-smooth-then-SVG-snap.
|
|
987
|
+
The cyan-300 (#67e8f9) target color matches R156's original
|
|
988
|
+
choice (legendAccent visual identity); only the transition
|
|
989
|
+
timing is new.
|
|
990
|
+
Note on cascade: SVG g elements rarely carry Tailwind
|
|
991
|
+
transition-* utility classes (Tailwind defaults are HTML-
|
|
992
|
+
centric), so the (0,1,0) specificity of .anet-topo-svg-focus
|
|
993
|
+
typically wins without !important. Adding !important defensively
|
|
994
|
+
anyway since the React component may add future inline styles
|
|
995
|
+
or Tailwind classes. Same trade-off as R490 (inline
|
|
996
|
+
style.transition still wins if added). */
|
|
997
|
+
.anet-topo-svg-focus {
|
|
998
|
+
outline: 2px solid transparent;
|
|
957
999
|
outline-offset: 2px;
|
|
1000
|
+
transition-property: outline-color !important;
|
|
1001
|
+
transition-duration: 200ms !important;
|
|
1002
|
+
transition-timing-function: ease-out !important;
|
|
1003
|
+
}
|
|
1004
|
+
.anet-topo-svg-focus:focus-visible {
|
|
1005
|
+
outline-color: #67e8f9;
|
|
958
1006
|
}
|
|
959
1007
|
|
|
960
1008
|
/* Round 46 — animated dashed spokes flowing from hub outward. Each spoke
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sleep2agi/agent-network-dashboard",
|
|
3
|
-
"version": "0.5.3-preview.
|
|
4
|
-
"description": "Agent Network Dashboard
|
|
3
|
+
"version": "0.5.3-preview.30",
|
|
4
|
+
"description": "Agent Network Dashboard \u2014 Web UI for managing AI Agent networks",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "next dev",
|
|
7
7
|
"build": "next build",
|
|
8
8
|
"start": "next start",
|
|
9
9
|
"lint": "eslint",
|
|
10
|
-
"prepublishOnly": "[ -f .next/BUILD_ID ] || (echo 'prepublishOnly: .next/BUILD_ID missing
|
|
10
|
+
"prepublishOnly": "[ -f .next/BUILD_ID ] || (echo 'prepublishOnly: .next/BUILD_ID missing \u2014 run npm run build first (see commit 05c1ebf body for R224 chunk-500 root cause)' >&2 && exit 1)"
|
|
11
11
|
},
|
|
12
12
|
"bin": {
|
|
13
13
|
"agent-network-dashboard": "./bin/start.js"
|
|
@@ -44,4 +44,4 @@
|
|
|
44
44
|
"tailwindcss": "^4",
|
|
45
45
|
"typescript": "^5"
|
|
46
46
|
}
|
|
47
|
-
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/* #157 fix verification (v0.10.8 lean ship — Fix #1 only per 通信龙 5573).
|
|
2
|
+
*
|
|
3
|
+
* Pre-fix the Servers panel showed:
|
|
4
|
+
* "agent rollup pending hub ≥ 0.8.2-preview"
|
|
5
|
+
* "disk metric pending hub ≥ 0.8.2-preview"
|
|
6
|
+
* commhub-server@0.8.2 is LIVE on prod but still doesn't ship `agents[]`
|
|
7
|
+
* or `disk_*` — the version-pinned text was misleading.
|
|
8
|
+
*
|
|
9
|
+
* Post-fix:
|
|
10
|
+
* "agent rollup not reported by hub"
|
|
11
|
+
* "disk metric not reported by hub"
|
|
12
|
+
* + data-server-agents-missing + data-server-disk-missing test attrs.
|
|
13
|
+
*
|
|
14
|
+
* Test:
|
|
15
|
+
* 1. Open dashboard, expand Servers drawer (localStorage flag)
|
|
16
|
+
* 2. Expand first server card (localStorage flag)
|
|
17
|
+
* 3. Assert visible copy lacks "0.8.2-preview"
|
|
18
|
+
* 4. Assert test-surface attrs present
|
|
19
|
+
* 5. Source-side regex confirms new copy + attrs wired
|
|
20
|
+
*/
|
|
21
|
+
import { chromium } from 'playwright';
|
|
22
|
+
import { readFileSync } from 'node:fs';
|
|
23
|
+
|
|
24
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
25
|
+
|
|
26
|
+
const browser = await chromium.launch({ headless: true });
|
|
27
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
|
|
28
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
29
|
+
await ctx.addInitScript(() => {
|
|
30
|
+
try {
|
|
31
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
32
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
33
|
+
// Force servers drawer open
|
|
34
|
+
localStorage.setItem('anet-servers-drawer', '1');
|
|
35
|
+
} catch {}
|
|
36
|
+
});
|
|
37
|
+
const page = await ctx.newPage();
|
|
38
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
|
|
39
|
+
await page.waitForTimeout(3500); // wait for SWR fetch + servers to render
|
|
40
|
+
|
|
41
|
+
// Expand first server card by clicking it
|
|
42
|
+
const firstCard = await page.$('[data-server-host], [data-server-card], button:has-text("iZ")');
|
|
43
|
+
// Simpler: just probe the drawer body text after expansion attempt
|
|
44
|
+
await page.evaluate(() => {
|
|
45
|
+
const buttons = Array.from(document.querySelectorAll('button'));
|
|
46
|
+
// Find server card expand toggles by their host text
|
|
47
|
+
const candidates = buttons.filter(b => /iZ|elaine/.test(b.textContent || ''));
|
|
48
|
+
if (candidates[0]) candidates[0].click();
|
|
49
|
+
});
|
|
50
|
+
await page.waitForTimeout(800);
|
|
51
|
+
|
|
52
|
+
const probe = await page.evaluate(() => {
|
|
53
|
+
// Probe drawer body for the placeholder text
|
|
54
|
+
const drawer = document.querySelector('[data-servers-body]') || document.body;
|
|
55
|
+
const text = drawer.textContent || '';
|
|
56
|
+
const agentsMissing = document.querySelectorAll('[data-server-agents-missing="true"]');
|
|
57
|
+
const diskMissing = document.querySelectorAll('[data-server-disk-missing="true"]');
|
|
58
|
+
return {
|
|
59
|
+
body_text_excerpt: text.slice(0, 1500),
|
|
60
|
+
has_stale_copy: /0\.8\.2-preview/.test(text),
|
|
61
|
+
has_new_agents_copy: /agent rollup not reported by hub/.test(text),
|
|
62
|
+
has_new_disk_copy: /disk metric not reported by hub/.test(text),
|
|
63
|
+
agents_missing_count: agentsMissing.length,
|
|
64
|
+
disk_missing_count: diskMissing.length,
|
|
65
|
+
};
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
await browser.close();
|
|
69
|
+
|
|
70
|
+
const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/ServersDrawer.tsx', 'utf8');
|
|
71
|
+
// The OLD strings should be gone from rendered text. The comment block keeps the
|
|
72
|
+
// historical "0.8.2-preview" reference for audit — that's OK because comments
|
|
73
|
+
// don't render. So source check: look for the NEW visible strings AND the test
|
|
74
|
+
// attrs, not for the absence of "0.8.2-preview".
|
|
75
|
+
const sourceNewAgentsCopy = /agent rollup not reported by hub/.test(src);
|
|
76
|
+
const sourceNewDiskCopy = /disk metric not reported by hub/.test(src);
|
|
77
|
+
const sourceAgentsAttr = /data-server-agents-missing="true"/.test(src);
|
|
78
|
+
const sourceDiskAttr = /data-server-disk-missing="true"/.test(src);
|
|
79
|
+
|
|
80
|
+
const results = {
|
|
81
|
+
dom_no_stale_copy: !probe.has_stale_copy,
|
|
82
|
+
dom_new_agents_copy: probe.has_new_agents_copy,
|
|
83
|
+
dom_new_disk_copy: probe.has_new_disk_copy,
|
|
84
|
+
agents_attr_present: probe.agents_missing_count > 0,
|
|
85
|
+
disk_attr_present: probe.disk_missing_count > 0,
|
|
86
|
+
source_new_agents: sourceNewAgentsCopy,
|
|
87
|
+
source_new_disk: sourceNewDiskCopy,
|
|
88
|
+
source_agents_attr: sourceAgentsAttr,
|
|
89
|
+
source_disk_attr: sourceDiskAttr,
|
|
90
|
+
};
|
|
91
|
+
const ok = Object.values(results).every(Boolean);
|
|
92
|
+
console.log(`${ok ? '✅' : '❌'} #157 servers copy fix:`, JSON.stringify(results),
|
|
93
|
+
'\n agents_missing_count:', probe.agents_missing_count, ' disk_missing_count:', probe.disk_missing_count,
|
|
94
|
+
'\n body_excerpt:', probe.body_text_excerpt.replace(/\s+/g, ' ').slice(0, 400));
|
|
95
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/* Round 500 verification: node alias text gains a status-coloured
|
|
2
|
+
* drop-shadow on hover (extends R476-R481 drop-shadow visual-polish
|
|
3
|
+
* family to 7th anchor). Pre-R500 hover triggered card-lift + alias
|
|
4
|
+
* letter-spacing; post-R500 the alias glyph itself glows.
|
|
5
|
+
*
|
|
6
|
+
* Test scenarios:
|
|
7
|
+
* 1. Rest state — no hover: data-node-alias-glow='false', no filter
|
|
8
|
+
* 2. Hover state — after synthetic pointerenter dispatch on g[data-node]
|
|
9
|
+
* descendant: glow attr 'true', computed filter contains drop-shadow
|
|
10
|
+
* 3. reducedMotion: glow attr 'false' regardless of hover (a11y)
|
|
11
|
+
* 4. Source-side regex confirms wiring
|
|
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
|
+
async function probe({ reducedMotion }) {
|
|
20
|
+
const browser = await chromium.launch({ headless: true });
|
|
21
|
+
const ctx = await browser.newContext({
|
|
22
|
+
viewport: { width: 1500, height: 1200 },
|
|
23
|
+
reducedMotion: reducedMotion ? 'reduce' : 'no-preference',
|
|
24
|
+
});
|
|
25
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
26
|
+
await ctx.addInitScript(() => {
|
|
27
|
+
try {
|
|
28
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
29
|
+
localStorage.setItem('anet-topo-layout', 'ring');
|
|
30
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
31
|
+
} catch {}
|
|
32
|
+
});
|
|
33
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
34
|
+
const r = await route.fetch();
|
|
35
|
+
const b = await r.json();
|
|
36
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
37
|
+
const mk = (alias, status) => ({
|
|
38
|
+
alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
39
|
+
network_id: nid, project_dir: null,
|
|
40
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
41
|
+
});
|
|
42
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
43
|
+
mk('alpha·a1', 'working'),
|
|
44
|
+
mk('alpha·a2', 'idle'),
|
|
45
|
+
] } });
|
|
46
|
+
});
|
|
47
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
48
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
49
|
+
const page = await ctx.newPage();
|
|
50
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
|
|
51
|
+
await page.waitForSelector('g[data-node]', { timeout: 15000 });
|
|
52
|
+
await page.waitForTimeout(1500);
|
|
53
|
+
|
|
54
|
+
// Phase 1: rest state
|
|
55
|
+
const rest = await page.evaluate(() => {
|
|
56
|
+
const text = document.querySelector('[data-node-alias-text]');
|
|
57
|
+
if (!text) return null;
|
|
58
|
+
return {
|
|
59
|
+
glow_attr: text.getAttribute('data-node-alias-glow'),
|
|
60
|
+
computed_filter: window.getComputedStyle(text).filter,
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Phase 2: hover state — synthetic pointerenter (R488 banked recipe)
|
|
65
|
+
const aliasAfterHover = await page.evaluate(() => {
|
|
66
|
+
const g = document.querySelector('g[data-node]');
|
|
67
|
+
if (!g) return null;
|
|
68
|
+
const alias = g.getAttribute('data-node');
|
|
69
|
+
const target = g.querySelector('circle, image, rect') || g;
|
|
70
|
+
['pointerenter', 'pointerover', 'mouseenter', 'mouseover'].forEach((t) => {
|
|
71
|
+
target.dispatchEvent(new Event(t, { bubbles: true, cancelable: true }));
|
|
72
|
+
});
|
|
73
|
+
return alias;
|
|
74
|
+
});
|
|
75
|
+
await page.waitForTimeout(400);
|
|
76
|
+
const hover = await page.evaluate(() => {
|
|
77
|
+
const text = document.querySelector('[data-node-alias-text]');
|
|
78
|
+
if (!text) return null;
|
|
79
|
+
return {
|
|
80
|
+
glow_attr: text.getAttribute('data-node-alias-glow'),
|
|
81
|
+
computed_filter: window.getComputedStyle(text).filter,
|
|
82
|
+
hovered_attr: text.getAttribute('data-node-alias-hovered'),
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
await browser.close();
|
|
87
|
+
return { rest, hover, alias: aliasAfterHover };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const motion = await probe({ reducedMotion: false });
|
|
91
|
+
const a11y = await probe({ reducedMotion: true });
|
|
92
|
+
|
|
93
|
+
const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
|
|
94
|
+
const sourceFilter = /filter: !reducedMotion && hoveredAlias === session\.alias\s*\?\s*`drop-shadow\(0 0 2px \$\{status\.text\}80\)`\s*:\s*undefined/.test(src);
|
|
95
|
+
const sourceAttr = /data-node-alias-glow=\{!reducedMotion && hoveredAlias === session\.alias \? 'true' : 'false'\}/.test(src);
|
|
96
|
+
const sourceTransition = /transition: 'fill 300ms ease-out, letter-spacing 200ms ease-out, filter 200ms ease-out'/.test(src);
|
|
97
|
+
|
|
98
|
+
const results = {
|
|
99
|
+
// Motion fixture, rest: attr='false', filter='none'
|
|
100
|
+
motion_rest_attr_false: motion.rest && motion.rest.glow_attr === 'false',
|
|
101
|
+
motion_rest_filter_none: motion.rest && motion.rest.computed_filter === 'none',
|
|
102
|
+
// Motion fixture, hover: attr='true', filter contains drop-shadow
|
|
103
|
+
motion_alias_resolved: !!motion.alias,
|
|
104
|
+
motion_hover_attr_true: motion.hover && motion.hover.glow_attr === 'true',
|
|
105
|
+
motion_hover_filter_drop:motion.hover && /drop-shadow/.test(motion.hover.computed_filter || ''),
|
|
106
|
+
// a11y fixture: attr='false' even after hover (component-side gate)
|
|
107
|
+
a11y_rest_attr_false: a11y.rest && a11y.rest.glow_attr === 'false',
|
|
108
|
+
a11y_hover_attr_false: a11y.hover && a11y.hover.glow_attr === 'false',
|
|
109
|
+
a11y_hover_filter_none: a11y.hover && a11y.hover.computed_filter === 'none',
|
|
110
|
+
// Source
|
|
111
|
+
source_filter_wired: sourceFilter,
|
|
112
|
+
source_attr_wired: sourceAttr,
|
|
113
|
+
source_transition_wired: sourceTransition,
|
|
114
|
+
};
|
|
115
|
+
const ok = Object.values(results).every(Boolean);
|
|
116
|
+
console.log(`${ok ? '✅' : '❌'} R500 node alias glow:`, JSON.stringify(results),
|
|
117
|
+
'\n motion rest :', JSON.stringify(motion.rest),
|
|
118
|
+
'\n motion hover:', JSON.stringify(motion.hover),
|
|
119
|
+
'\n a11y rest :', JSON.stringify(a11y.rest),
|
|
120
|
+
'\n a11y hover:', JSON.stringify(a11y.hover));
|
|
121
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/* Round 501 verification: vendor avatar <image> inside node circles
|
|
2
|
+
* gains a hover-gated `filter: brightness(1.15)`. Closes the per-node
|
|
3
|
+
* hover-affordance arc — every per-node element type now has a hover
|
|
4
|
+
* treatment.
|
|
5
|
+
*
|
|
6
|
+
* Test fixture: claude-code-cli runtime → vendor.logo path renders the
|
|
7
|
+
* <image> branch (vs monogram fallback). Verifies:
|
|
8
|
+
* 1. rest: data-node-avatar-hovered='false', no brightness filter
|
|
9
|
+
* 2. hover (synthetic pointerenter): attr='true', computed filter
|
|
10
|
+
* contains brightness(1.15)
|
|
11
|
+
* 3. a11y: attr='false' even after hover (component gate)
|
|
12
|
+
* 4. source-side regex confirms wiring
|
|
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
|
+
async function probe({ reducedMotion }) {
|
|
21
|
+
const browser = await chromium.launch({ headless: true });
|
|
22
|
+
const ctx = await browser.newContext({
|
|
23
|
+
viewport: { width: 1500, height: 1200 },
|
|
24
|
+
reducedMotion: reducedMotion ? 'reduce' : 'no-preference',
|
|
25
|
+
});
|
|
26
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
27
|
+
await ctx.addInitScript(() => {
|
|
28
|
+
try {
|
|
29
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
30
|
+
localStorage.setItem('anet-topo-layout', 'ring');
|
|
31
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
32
|
+
} catch {}
|
|
33
|
+
});
|
|
34
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
35
|
+
const r = await route.fetch();
|
|
36
|
+
const b = await r.json();
|
|
37
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
38
|
+
const mk = (alias, status) => ({
|
|
39
|
+
// claude-code-cli renders the avatar <image> branch
|
|
40
|
+
alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
41
|
+
network_id: nid, project_dir: null,
|
|
42
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
43
|
+
});
|
|
44
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
45
|
+
mk('alpha·a1', 'working'),
|
|
46
|
+
mk('alpha·a2', 'idle'),
|
|
47
|
+
] } });
|
|
48
|
+
});
|
|
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('g[data-node]', { timeout: 15000 });
|
|
54
|
+
await page.waitForTimeout(1500);
|
|
55
|
+
|
|
56
|
+
const rest = await page.evaluate(() => {
|
|
57
|
+
const img = document.querySelector('[data-node-avatar]');
|
|
58
|
+
if (!img) return { found: false };
|
|
59
|
+
return {
|
|
60
|
+
found: true,
|
|
61
|
+
hovered_attr: img.getAttribute('data-node-avatar-hovered'),
|
|
62
|
+
computed_filter: window.getComputedStyle(img).filter,
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Synthetic hover via R488 banked recipe
|
|
67
|
+
await page.evaluate(() => {
|
|
68
|
+
const g = document.querySelector('g[data-node]');
|
|
69
|
+
if (!g) return;
|
|
70
|
+
const target = g.querySelector('circle, image, rect') || g;
|
|
71
|
+
['pointerenter', 'pointerover', 'mouseenter', 'mouseover'].forEach((t) => {
|
|
72
|
+
target.dispatchEvent(new Event(t, { bubbles: true, cancelable: true }));
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
await page.waitForTimeout(400);
|
|
76
|
+
|
|
77
|
+
const hover = await page.evaluate(() => {
|
|
78
|
+
const img = document.querySelector('[data-node-avatar]');
|
|
79
|
+
if (!img) return { found: false };
|
|
80
|
+
return {
|
|
81
|
+
found: true,
|
|
82
|
+
hovered_attr: img.getAttribute('data-node-avatar-hovered'),
|
|
83
|
+
computed_filter: window.getComputedStyle(img).filter,
|
|
84
|
+
};
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
await browser.close();
|
|
88
|
+
return { rest, hover };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const motion = await probe({ reducedMotion: false });
|
|
92
|
+
const a11y = await probe({ reducedMotion: true });
|
|
93
|
+
|
|
94
|
+
const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
|
|
95
|
+
const sourceGate = /const isAvatarHovered = !reducedMotion && hoveredAlias === session\.alias;/.test(src);
|
|
96
|
+
const sourceFilter = /filter: isAvatarHovered \? 'brightness\(1\.15\)' : undefined/.test(src);
|
|
97
|
+
const sourceAttr = /data-node-avatar-hovered=\{isAvatarHovered \? 'true' : 'false'\}/.test(src);
|
|
98
|
+
|
|
99
|
+
const results = {
|
|
100
|
+
motion_rest_found: motion.rest.found,
|
|
101
|
+
motion_rest_attr_false: motion.rest.found && motion.rest.hovered_attr === 'false',
|
|
102
|
+
motion_rest_filter_none: motion.rest.found && motion.rest.computed_filter === 'none',
|
|
103
|
+
motion_hover_attr_true: motion.hover.found && motion.hover.hovered_attr === 'true',
|
|
104
|
+
motion_hover_filter_bright: motion.hover.found && /brightness\(1\.15\)/.test(motion.hover.computed_filter || ''),
|
|
105
|
+
a11y_rest_attr_false: a11y.rest.found && a11y.rest.hovered_attr === 'false',
|
|
106
|
+
a11y_hover_attr_false: a11y.hover.found && a11y.hover.hovered_attr === 'false',
|
|
107
|
+
a11y_hover_filter_none: a11y.hover.found && a11y.hover.computed_filter === 'none',
|
|
108
|
+
source_gate_wired: sourceGate,
|
|
109
|
+
source_filter_wired: sourceFilter,
|
|
110
|
+
source_attr_wired: sourceAttr,
|
|
111
|
+
};
|
|
112
|
+
const ok = Object.values(results).every(Boolean);
|
|
113
|
+
console.log(`${ok ? '✅' : '❌'} R501 avatar brightness:`, JSON.stringify(results),
|
|
114
|
+
'\n motion:', JSON.stringify(motion),
|
|
115
|
+
'\n a11y :', JSON.stringify(a11y));
|
|
116
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/* Round 494 verification: chip-row working + online chips gain
|
|
2
|
+
* `active:scale-95` press feedback, gated on the clickable branch
|
|
3
|
+
* (workingCount > 0 / onlineNodes.length > 0). Extends the chrome-
|
|
4
|
+
* strip press family (R492 Ring/Grid + R493 5 chrome buttons) into
|
|
5
|
+
* the chip-row scope.
|
|
6
|
+
*
|
|
7
|
+
* Verifies per chip:
|
|
8
|
+
* - DOM element resolvable (data-working-chip / data-online-chip)
|
|
9
|
+
* - className contains `active:scale-95` (since fixture has both chips
|
|
10
|
+
* with > 0 count → clickable branch active)
|
|
11
|
+
* - computed transition-property includes `transform`
|
|
12
|
+
* - source-file regex confirms class string 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
|
+
// Need both working and online > 0 so both chip variants enter clickable branch
|
|
40
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
41
|
+
mk('alpha·a1', 'working'),
|
|
42
|
+
mk('alpha·a2', 'idle'),
|
|
43
|
+
] } });
|
|
44
|
+
});
|
|
45
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
46
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
47
|
+
const page = await ctx.newPage();
|
|
48
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
|
|
49
|
+
await page.waitForSelector('[data-working-chip]', { timeout: 15000 });
|
|
50
|
+
await page.waitForTimeout(1000);
|
|
51
|
+
|
|
52
|
+
const probe = async (sel) => {
|
|
53
|
+
return await page.evaluate((s) => {
|
|
54
|
+
const el = document.querySelector(s);
|
|
55
|
+
if (!el) return null;
|
|
56
|
+
const cs = window.getComputedStyle(el);
|
|
57
|
+
return {
|
|
58
|
+
cls: el.className || '',
|
|
59
|
+
cls_has_scale95: /active:scale-95/.test(el.className || ''),
|
|
60
|
+
cls_has_translate: /hover:-translate-y-px/.test(el.className || ''),
|
|
61
|
+
tp: cs.transitionProperty,
|
|
62
|
+
td: cs.transitionDuration,
|
|
63
|
+
tp_has_transform: /transform/i.test(cs.transitionProperty || ''),
|
|
64
|
+
};
|
|
65
|
+
}, sel);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const wInfo = await probe('[data-working-chip]');
|
|
69
|
+
const oInfo = await probe('[data-online-chip]');
|
|
70
|
+
|
|
71
|
+
await browser.close();
|
|
72
|
+
|
|
73
|
+
const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
|
|
74
|
+
const sWorking = /workingCount > 0\s*\?\s*'bg-green-500\/10 text-green-300 border-green-500\/20 hover:bg-green-500\/15 hover:border-green-500\/30 hover:-translate-y-px active:scale-95'/.test(src);
|
|
75
|
+
const sOnline = /onlineNodes\.length > 0\s*\?\s*'bg-cyan-500\/10 text-cyan-300 border-cyan-500\/20 hover:bg-cyan-500\/15 hover:border-cyan-500\/30 hover:-translate-y-px active:scale-95'/.test(src);
|
|
76
|
+
|
|
77
|
+
const results = {
|
|
78
|
+
working_dom_found: !!wInfo,
|
|
79
|
+
working_has_scale95: wInfo && wInfo.cls_has_scale95,
|
|
80
|
+
working_has_lift: wInfo && wInfo.cls_has_translate,
|
|
81
|
+
working_tp_transform: wInfo && wInfo.tp_has_transform,
|
|
82
|
+
online_dom_found: !!oInfo,
|
|
83
|
+
online_has_scale95: oInfo && oInfo.cls_has_scale95,
|
|
84
|
+
online_has_lift: oInfo && oInfo.cls_has_translate,
|
|
85
|
+
online_tp_transform: oInfo && oInfo.tp_has_transform,
|
|
86
|
+
source_working_wired: sWorking,
|
|
87
|
+
source_online_wired: sOnline,
|
|
88
|
+
};
|
|
89
|
+
const ok = Object.values(results).every(Boolean);
|
|
90
|
+
console.log(`${ok ? '✅' : '❌'} chip-row working+online active:scale-95 (R494):`, JSON.stringify(results),
|
|
91
|
+
'\n working tp:', wInfo && wInfo.tp,
|
|
92
|
+
'\n online tp:', oInfo && oInfo.tp);
|
|
93
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/* Round 493 verification: chrome-strip active:scale-95 press feedback
|
|
2
|
+
* family rolls out from Ring/Grid (R492) to the remaining 5 chrome
|
|
3
|
+
* buttons: nodeSize S/M/L (1 selector per .map call — 3 buttons),
|
|
4
|
+
* zoom-out, zoom-in, reset, fullscreen.
|
|
5
|
+
*
|
|
6
|
+
* Total chrome strip active:scale-95 coverage after R493 = 7 buttons
|
|
7
|
+
* (R306-era 7-button family unified on press feedback).
|
|
8
|
+
*
|
|
9
|
+
* Verifies per button:
|
|
10
|
+
* - DOM element resolvable
|
|
11
|
+
* - className contains `active:scale-95`
|
|
12
|
+
* - computed transition-property includes `transform`
|
|
13
|
+
* - computed transition-duration is 0.2s (200ms)
|
|
14
|
+
* - source-file regex confirms the class string wired
|
|
15
|
+
*/
|
|
16
|
+
import { chromium } from 'playwright';
|
|
17
|
+
import { readFileSync } from 'node:fs';
|
|
18
|
+
|
|
19
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
20
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
21
|
+
|
|
22
|
+
const browser = await chromium.launch({ headless: true });
|
|
23
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
|
|
24
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
25
|
+
await ctx.addInitScript(() => {
|
|
26
|
+
try {
|
|
27
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
28
|
+
localStorage.setItem('anet-topo-layout', 'ring');
|
|
29
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
30
|
+
} catch {}
|
|
31
|
+
});
|
|
32
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
33
|
+
const r = await route.fetch();
|
|
34
|
+
const b = await r.json();
|
|
35
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
36
|
+
const mk = (alias, status) => ({
|
|
37
|
+
alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
38
|
+
network_id: nid, project_dir: null,
|
|
39
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
40
|
+
});
|
|
41
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [mk('a·1', 'working')] } });
|
|
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('[data-topo-chrome-fullscreen]', { timeout: 15000 });
|
|
48
|
+
await page.waitForTimeout(1000);
|
|
49
|
+
|
|
50
|
+
const probe = async (selector, label) => {
|
|
51
|
+
const data = await page.evaluate((s) => {
|
|
52
|
+
const els = Array.from(document.querySelectorAll(s));
|
|
53
|
+
if (!els.length) return { found: 0 };
|
|
54
|
+
return {
|
|
55
|
+
found: els.length,
|
|
56
|
+
samples: els.slice(0, 3).map((el) => {
|
|
57
|
+
const cs = window.getComputedStyle(el);
|
|
58
|
+
return {
|
|
59
|
+
cls_has_scale95: /active:scale-95/.test(el.className || ''),
|
|
60
|
+
tp_has_transform: /transform/i.test(cs.transitionProperty || ''),
|
|
61
|
+
td_includes_200: /\b0\.2s\b/.test(cs.transitionDuration || ''),
|
|
62
|
+
tp: cs.transitionProperty,
|
|
63
|
+
td: cs.transitionDuration,
|
|
64
|
+
};
|
|
65
|
+
}),
|
|
66
|
+
};
|
|
67
|
+
}, selector);
|
|
68
|
+
return { label, ...data };
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const nodeSizeBtns = await probe('button[data-topo-chrome-node-size]', 'nodeSize');
|
|
72
|
+
// node-size buttons may use a different attr; fall back to nth child selector
|
|
73
|
+
const zoomOutBtn = await probe('button[title^="Zoom out"]', 'zoomOut');
|
|
74
|
+
const zoomInBtn = await probe('button[title^="Zoom in"]', 'zoomIn');
|
|
75
|
+
const resetBtn = await probe('button[data-topo-chrome-reset-hover-lift]', 'reset');
|
|
76
|
+
const fullBtn = await probe('button[data-topo-chrome-fullscreen]', 'fullscreen');
|
|
77
|
+
|
|
78
|
+
await browser.close();
|
|
79
|
+
|
|
80
|
+
const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
|
|
81
|
+
// 5 source-side regex anchors (one per button class string)
|
|
82
|
+
const sNodeSize = /px-2 py-1 transition-colors transition-transform duration-200 ease-out transform-gpu active:scale-95/.test(src);
|
|
83
|
+
const sZoom = /group px-2 py-1 hover:bg-white\/5 active:bg-white\/10 transition-colors transition-transform duration-200 ease-out transform-gpu active:scale-95/.test(src);
|
|
84
|
+
const sReset = /p-1\.5 rounded-md border hover:bg-white\/5 active:bg-white\/10 hover:-translate-y-px active:scale-95 transition-colors transition-transform duration-200/.test(src);
|
|
85
|
+
const sFullscreen= /group p-1\.5 rounded-md border hover:-translate-y-px active:scale-95 transition-colors transition-transform duration-200/.test(src);
|
|
86
|
+
|
|
87
|
+
const allButtonsPressReady = (info) =>
|
|
88
|
+
info.found > 0 && info.samples.every((s) => s.cls_has_scale95 && s.tp_has_transform && s.td_includes_200);
|
|
89
|
+
|
|
90
|
+
const results = {
|
|
91
|
+
zoom_out_press_ready: allButtonsPressReady(zoomOutBtn),
|
|
92
|
+
zoom_in_press_ready: allButtonsPressReady(zoomInBtn),
|
|
93
|
+
reset_press_ready: allButtonsPressReady(resetBtn),
|
|
94
|
+
fullscreen_press_ready:allButtonsPressReady(fullBtn),
|
|
95
|
+
source_nodesize_wired: sNodeSize,
|
|
96
|
+
source_zoom_wired: sZoom,
|
|
97
|
+
source_reset_wired: sReset,
|
|
98
|
+
source_fullscreen_wired: sFullscreen,
|
|
99
|
+
};
|
|
100
|
+
const ok = Object.values(results).every(Boolean);
|
|
101
|
+
console.log(`${ok ? '✅' : '❌'} chrome-strip 5-button press feedback (R493):`, JSON.stringify(results),
|
|
102
|
+
'\n zoomOut found:', zoomOutBtn.found, ' zoomIn:', zoomInBtn.found,
|
|
103
|
+
'\n reset:', resetBtn.found, ' fullscreen:', fullBtn.found,
|
|
104
|
+
'\n nodeSize found:', nodeSizeBtns.found);
|
|
105
|
+
process.exit(ok ? 0 : 1);
|