@sleep2agi/agent-network-dashboard 0.4.6-preview.39 → 0.4.6-preview.396
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/app-path-routes-manifest.json +2 -0
- package/.next/build-manifest.json +3 -3
- package/.next/diagnostics/route-bundle-stats.json +41 -41
- package/.next/fallback-build-manifest.json +3 -3
- package/.next/prerender-manifest.json +3 -3
- package/.next/routes-manifest.json +16 -0
- 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/api/hub/server/[hostname]/agents/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/hub/server/[hostname]/agents/route/build-manifest.json +9 -0
- package/.next/server/app/api/hub/server/[hostname]/agents/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/hub/server/[hostname]/agents/route.js +7 -0
- package/.next/server/app/api/hub/server/[hostname]/agents/route.js.map +5 -0
- package/.next/server/app/api/hub/server/[hostname]/agents/route.js.nft.json +1 -0
- package/.next/server/app/api/hub/server/[hostname]/agents/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/hub/server/[hostname]/health/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/hub/server/[hostname]/health/route/build-manifest.json +9 -0
- package/.next/server/app/api/hub/server/[hostname]/health/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/hub/server/[hostname]/health/route.js +7 -0
- package/.next/server/app/api/hub/server/[hostname]/health/route.js.map +5 -0
- package/.next/server/app/api/hub/server/[hostname]/health/route.js.nft.json +1 -0
- package/.next/server/app/api/hub/server/[hostname]/health/route_client-reference-manifest.js +3 -0
- 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/app-paths-manifest.json +2 -0
- package/.next/server/chunks/0dk5_next-internal_server_app_api_hub_server_[hostname]_agents_route_actions_0vr92s4.js +3 -0
- package/.next/server/chunks/0dk5_next-internal_server_app_api_hub_server_[hostname]_agents_route_actions_0vr92s4.js.map +1 -0
- package/.next/server/chunks/0dk5_next-internal_server_app_api_hub_server_[hostname]_health_route_actions_0vr-bv5.js +3 -0
- package/.next/server/chunks/0dk5_next-internal_server_app_api_hub_server_[hostname]_health_route_actions_0vr-bv5.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__05nyivw._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__05nyivw._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0floe3h._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0floe3h._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +4 -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_057q.ne._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.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_0_d45-d._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.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/server/server-reference-manifest.js +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/0-mpa_947ipeq.js +1 -0
- package/.next/static/chunks/01lmbsd37fybu.js +1 -0
- package/.next/static/chunks/03o.h6kvmw4l_.js +1 -0
- package/.next/static/chunks/03~~oirxz7~vc.js +1 -0
- package/.next/static/chunks/05uk96gc~9mni.js +1 -0
- package/.next/static/chunks/06nb5y2kl41td.js +1 -0
- package/.next/static/chunks/08kf-.bgs10ze.js +1 -0
- package/.next/static/chunks/0aauz~36q5n2a.css +2 -0
- package/.next/static/chunks/0glmrdj9112tc.js +7 -0
- package/.next/static/chunks/0l_~q07bhpkcx.js +7 -0
- package/.next/static/chunks/0ql1lg_6gwlv8.js +4 -0
- package/.next/static/chunks/0rxrez_cjn.eh.js +1 -0
- package/.next/static/chunks/0x8x.29hkju5a.js +1 -0
- package/.next/static/chunks/117g0j1~f0vr..js +1 -0
- package/.next/static/chunks/174re5ar~kxne.js +1 -0
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/.next/types/routes.d.ts +3 -1
- package/.next/types/validator.ts +18 -0
- package/app/api/hub/server/[hostname]/agents/route.ts +62 -0
- package/app/api/hub/server/[hostname]/health/route.ts +58 -0
- package/app/api/hub/servers/route.ts +88 -1
- package/app/components/ChatPopover.tsx +38 -2
- package/app/components/CommandPalette.tsx +107 -0
- package/app/components/HelpOverlay.tsx +109 -1
- package/app/components/ServersDrawer.tsx +181 -5
- package/app/components/TopoGraph.tsx +6492 -270
- package/app/components/utils.ts +8 -7
- package/app/globals.css +110 -5
- package/app/lib/time.ts +36 -0
- package/app/lib/vendorIdentity.ts +14 -3
- package/docs/rfc/v0.10.0-hero3-dashboard-upgrade.md +230 -0
- package/package.json +2 -2
- package/public/sleep2agi-logo.svg +11 -0
- package/public/vendors/claude.svg +11 -0
- package/public/vendors/minimax.svg +11 -0
- package/public/vendors/openai.svg +12 -0
- package/screenshots/r293-p0/after-full.png +0 -0
- package/screenshots/r293-p0/after-topo.png +0 -0
- package/screenshots/r293-p0/baseline-full.png +0 -0
- package/screenshots/r293-p0/baseline-topo.png +0 -0
- package/screenshots/r293-p0/logo-integrated-full.png +0 -0
- package/screenshots/r293-p0/logo-integrated-topo.png +0 -0
- package/screenshots/r293-p0/servers-drawer-v0.10.0-full.png +0 -0
- package/screenshots/r293-p0/servers-drawer-v0.10.0.png +0 -0
- package/screenshots/r293-p0/wave1-after-full.png +0 -0
- package/screenshots/r293-p0/wave1-after-topo.png +0 -0
- package/scripts/p0-servers-drawer-screenshot.mjs +99 -0
- package/scripts/p0-topo-screenshot.mjs +82 -0
- package/scripts/topo-active-filter-pill-test.mjs +101 -0
- package/scripts/topo-active-links-chip-hover-test.mjs +103 -0
- package/scripts/topo-active-links-chip-nav-test.mjs +117 -0
- package/scripts/topo-active-links-chip-self-hover-test.mjs +171 -0
- package/scripts/topo-active-links-chip-test.mjs +73 -0
- package/scripts/topo-active-links-empty-recede-test.mjs +125 -0
- package/scripts/topo-active-links-freshness-test.mjs +113 -0
- package/scripts/topo-active-links-tooltip-test.mjs +79 -0
- package/scripts/topo-active-pulse-ease-test.mjs +120 -0
- package/scripts/topo-brand-logo-transition-test.mjs +94 -0
- package/scripts/topo-brand-watermark-test.mjs +97 -0
- package/scripts/topo-canvas-aria-test.mjs +108 -0
- package/scripts/topo-canvas-brand-fade-test.mjs +87 -0
- package/scripts/topo-chat-target-breath-test.mjs +84 -0
- package/scripts/topo-chat-target-ring-ease-test.mjs +82 -0
- package/scripts/topo-chat-target-ring-fade-test.mjs +124 -0
- package/scripts/topo-chip-empty-recede-test.mjs +116 -0
- package/scripts/topo-chip-focus-test.mjs +95 -0
- package/scripts/topo-chip-pin-mirror-test.mjs +113 -0
- package/scripts/topo-chip-row-gap-test.mjs +90 -0
- package/scripts/topo-chip-row-semantic-gap-test.mjs +97 -0
- package/scripts/topo-chip-row-tabular-test.mjs +111 -0
- package/scripts/topo-chiprow-data-weight-test.mjs +94 -0
- package/scripts/topo-chrome-a11y-test.mjs +107 -0
- package/scripts/topo-chrome-active-weight-test.mjs +104 -0
- package/scripts/topo-chrome-focus-ring-unify-test.mjs +100 -0
- package/scripts/topo-chrome-gap-test.mjs +73 -0
- package/scripts/topo-chrome-icon-stroke-test.mjs +95 -0
- package/scripts/topo-chrome-inactive-color-test.mjs +90 -0
- package/scripts/topo-chrome-pop-extension-test.mjs +98 -0
- package/scripts/topo-chrome-pop-nodesize-test.mjs +88 -0
- package/scripts/topo-chrome-press-state-test.mjs +135 -0
- package/scripts/topo-chrome-strip-semantic-gap-test.mjs +99 -0
- package/scripts/topo-chrome-strip-theme-ease-test.mjs +99 -0
- package/scripts/topo-chrome-zoom-level-pop-test.mjs +122 -0
- package/scripts/topo-chrome-zoom-pop-test.mjs +130 -0
- package/scripts/topo-chrome-zoom-readout-weight-test.mjs +81 -0
- package/scripts/topo-click-ripple-ease-test.mjs +94 -0
- package/scripts/topo-cmdk-edge-clear-test.mjs +109 -0
- package/scripts/topo-cmdk-layout-view-test.mjs +108 -0
- package/scripts/topo-cmdk-pin-test.mjs +123 -0
- package/scripts/topo-cmdk-vendor-clear-test.mjs +133 -0
- package/scripts/topo-cmdk-vendor-pin-test.mjs +103 -0
- package/scripts/topo-corner-radius-unify-test.mjs +103 -0
- package/scripts/topo-cwd-tooltip-test.mjs +64 -0
- package/scripts/topo-dblclick-reset-test.mjs +74 -0
- package/scripts/topo-dense-alias-fill-transition-test.mjs +133 -0
- package/scripts/topo-discrete-zoom-smooth-test.mjs +125 -0
- package/scripts/topo-edge-badge-click-pin-test.mjs +94 -0
- package/scripts/topo-edge-badge-crossfade-test.mjs +138 -0
- package/scripts/topo-edge-badge-hot-test.mjs +115 -0
- package/scripts/topo-edge-badge-hover-test.mjs +85 -0
- package/scripts/topo-edge-badge-lift-test.mjs +150 -0
- package/scripts/topo-edge-badge-ripple-test.mjs +111 -0
- package/scripts/topo-edge-badge-stroke-transition-test.mjs +118 -0
- package/scripts/topo-edge-badge-text-pin-test.mjs +121 -0
- package/scripts/topo-edge-badge-theme-ease-test.mjs +89 -0
- package/scripts/topo-edge-count-badge-test.mjs +86 -0
- package/scripts/topo-edge-endpoint-highlight-test.mjs +111 -0
- package/scripts/topo-edge-fadein-test.mjs +106 -0
- package/scripts/topo-edge-focus-test.mjs +113 -0
- package/scripts/topo-edge-hover-test.mjs +93 -0
- package/scripts/topo-edge-pill-hot-test.mjs +129 -0
- package/scripts/topo-edge-pill-test.mjs +105 -0
- package/scripts/topo-edge-ping-pulse-retired-test.mjs +119 -0
- package/scripts/topo-edge-stroke-ease-test.mjs +100 -0
- package/scripts/topo-edge-stroke-transition-test.mjs +132 -0
- package/scripts/topo-edge-tooltip-test.mjs +70 -0
- package/scripts/topo-empty-hint-tracking-test.mjs +84 -0
- package/scripts/topo-empty-state-breath-test.mjs +150 -0
- package/scripts/topo-empty-state-crossfade-test.mjs +130 -0
- package/scripts/topo-empty-state-recenter-test.mjs +107 -0
- package/scripts/topo-empty-subhint-tracking-test.mjs +87 -0
- package/scripts/topo-endpoint-ring-fade-test.mjs +132 -0
- package/scripts/topo-endpoint-ring-test.mjs +93 -0
- package/scripts/topo-endpoint-ring-thicken-test.mjs +112 -0
- package/scripts/topo-esc-clears-pin-test.mjs +98 -0
- package/scripts/topo-filter-pill-aliases-test.mjs +121 -0
- package/scripts/topo-filter-pill-body-click-test.mjs +112 -0
- package/scripts/topo-filter-pill-count-tabular-test.mjs +135 -0
- package/scripts/topo-filter-pill-count-test.mjs +110 -0
- package/scripts/topo-filter-pill-mobile-test.mjs +90 -0
- package/scripts/topo-freshness-chip-test.mjs +74 -0
- package/scripts/topo-freshness-chip-transition-test.mjs +80 -0
- package/scripts/topo-freshness-chip-weight-test.mjs +90 -0
- package/scripts/topo-freshness-conditional-render-test.mjs +92 -0
- package/scripts/topo-fullscreen-active-test.mjs +115 -0
- package/scripts/topo-group-box-pinned-state-test.mjs +95 -0
- package/scripts/topo-group-count-test.mjs +6 -1
- package/scripts/topo-group-label-a11y-test.mjs +113 -0
- package/scripts/topo-group-label-count-inherit-test.mjs +99 -0
- package/scripts/topo-group-label-hover-test.mjs +119 -0
- package/scripts/topo-group-label-pin-spacing-test.mjs +136 -0
- package/scripts/topo-group-label-tint-test.mjs +99 -0
- package/scripts/topo-group-label-title-test.mjs +83 -0
- package/scripts/topo-group-pin-test.mjs +119 -0
- package/scripts/topo-group-pip-fadein-test.mjs +104 -0
- package/scripts/topo-group-pip-singletier-drop-test.mjs +131 -0
- package/scripts/topo-group-pip-tabular-test.mjs +95 -0
- package/scripts/topo-group-status-pips-test.mjs +103 -0
- package/scripts/topo-groupbox-fadein-test.mjs +95 -0
- package/scripts/topo-groupbox-hover-lift-test.mjs +127 -0
- package/scripts/topo-groupbox-hubcore-ease-test.mjs +106 -0
- package/scripts/topo-groupbox-live-test.mjs +100 -0
- package/scripts/topo-groupbox-march-busyness-test.mjs +155 -0
- package/scripts/topo-groupbox-test.mjs +4 -2
- package/scripts/topo-header-gap-test.mjs +94 -0
- package/scripts/topo-help-overlay-r109-test.mjs +67 -0
- package/scripts/topo-help-overlay-r118-test.mjs +69 -0
- package/scripts/topo-help-overlay-r123-test.mjs +69 -0
- package/scripts/topo-help-overlay-r134-test.mjs +74 -0
- package/scripts/topo-help-overlay-r137-test.mjs +81 -0
- package/scripts/topo-help-overlay-r141-test.mjs +86 -0
- package/scripts/topo-help-overlay-r153-test.mjs +85 -0
- package/scripts/topo-help-overlay-r59-test.mjs +67 -0
- package/scripts/topo-help-overlay-r65-test.mjs +64 -0
- package/scripts/topo-help-overlay-r70-test.mjs +63 -0
- package/scripts/topo-help-overlay-r78-test.mjs +59 -0
- package/scripts/topo-help-overlay-r81-test.mjs +58 -0
- package/scripts/topo-help-overlay-r87-test.mjs +64 -0
- package/scripts/topo-help-overlay-r91-test.mjs +65 -0
- package/scripts/topo-hot-tail-crossfade-test.mjs +117 -0
- package/scripts/topo-html-overlay-padding-unify-test.mjs +120 -0
- package/scripts/topo-hub-a11y-test.mjs +112 -0
- package/scripts/topo-hub-busyness-test.mjs +100 -0
- package/scripts/topo-hub-center-crossfade-test.mjs +124 -0
- package/scripts/topo-hub-click-fit-test.mjs +137 -0
- package/scripts/topo-hub-digit-scale-test.mjs +129 -0
- package/scripts/topo-hub-fadein-test.mjs +88 -0
- package/scripts/topo-hub-hover-lift-test.mjs +101 -0
- package/scripts/topo-hub-hover-ring-test.mjs +82 -0
- package/scripts/topo-hub-pip-theme-ease-test.mjs +116 -0
- package/scripts/topo-hub-spoke-ease-test.mjs +120 -0
- package/scripts/topo-hub-title-test.mjs +93 -0
- package/scripts/topo-hub-working-count-test.mjs +99 -0
- package/scripts/topo-ingroup-edge-test.mjs +103 -0
- package/scripts/topo-inner-radar-ring-retired-test.mjs +89 -0
- package/scripts/topo-kicker-color-test.mjs +93 -0
- package/scripts/topo-kicker-tracking-test.mjs +101 -0
- package/scripts/topo-kicker-weight-test.mjs +98 -0
- package/scripts/topo-label-card-theme-ease-test.mjs +85 -0
- package/scripts/topo-label-lift-test.mjs +5 -1
- package/scripts/topo-lastseen-tooltip-test.mjs +59 -0
- package/scripts/topo-layout-hover-text-preview-test.mjs +101 -0
- package/scripts/topo-layout-switch-crossfade-test.mjs +126 -0
- package/scripts/topo-layout-toggle-border-unify-test.mjs +104 -0
- package/scripts/topo-layout-toggle-test.mjs +131 -0
- package/scripts/topo-layout-trailer-mr-test.mjs +82 -0
- package/scripts/topo-legend-compress-test.mjs +107 -0
- package/scripts/topo-legend-count-empty-test.mjs +126 -0
- package/scripts/topo-legend-count-tabular-parity-test.mjs +90 -0
- package/scripts/topo-legend-count-tabular-test.mjs +87 -0
- package/scripts/topo-legend-count-test.mjs +88 -0
- package/scripts/topo-legend-count-tier-fill-test.mjs +121 -0
- package/scripts/topo-legend-count-weight-test.mjs +86 -0
- package/scripts/topo-legend-header-symmetric-inset-test.mjs +112 -0
- package/scripts/topo-legend-hitbox-swatch-center-test.mjs +93 -0
- package/scripts/topo-legend-hover-test.mjs +113 -0
- package/scripts/topo-legend-offline-delimiter-test.mjs +84 -0
- package/scripts/topo-legend-offline-label-simplify-test.mjs +86 -0
- package/scripts/topo-legend-panel-count-weight-test.mjs +89 -0
- package/scripts/topo-legend-panel-header-test.mjs +83 -0
- package/scripts/topo-legend-pin-ring-fade-test.mjs +112 -0
- package/scripts/topo-legend-pin-test.mjs +128 -0
- package/scripts/topo-legend-row-hover-lift-test.mjs +109 -0
- package/scripts/topo-legend-row-labels-simplify-test.mjs +80 -0
- package/scripts/topo-legend-row-pin-spacing-test.mjs +124 -0
- package/scripts/topo-legend-row-tint-test.mjs +95 -0
- package/scripts/topo-legend-row-title-test.mjs +102 -0
- package/scripts/topo-legend-swatch-grow-test.mjs +116 -0
- package/scripts/topo-legend-swatch-radius-test.mjs +78 -0
- package/scripts/topo-minimap-a11y-test.mjs +101 -0
- package/scripts/topo-minimap-dot-transition-test.mjs +113 -0
- package/scripts/topo-minimap-radius-test.mjs +88 -0
- package/scripts/topo-minimap-rect-stroke-test.mjs +94 -0
- package/scripts/topo-minimap-viewport-smooth-test.mjs +124 -0
- package/scripts/topo-mobile-chips-test.mjs +103 -0
- package/scripts/topo-monogram-font-test.mjs +112 -0
- package/scripts/topo-monogram-stroke-test.mjs +101 -0
- package/scripts/topo-more-flows-letterspacing-test.mjs +111 -0
- package/scripts/topo-node-a11y-test.mjs +107 -0
- package/scripts/topo-node-alias-chat-pin-test.mjs +95 -0
- package/scripts/topo-node-hover-lift-test.mjs +85 -0
- package/scripts/topo-node-label-card-elevation-test.mjs +157 -0
- package/scripts/topo-node-label-card-stroke-test.mjs +135 -0
- package/scripts/topo-node-stroke-transition-test.mjs +103 -0
- package/scripts/topo-node-text-fill-transition-test.mjs +119 -0
- package/scripts/topo-node-title-peers-test.mjs +107 -0
- package/scripts/topo-node-title-test.mjs +101 -0
- package/scripts/topo-nodesize-active-hover-test.mjs +109 -0
- package/scripts/topo-nodesize-crossfade-test.mjs +141 -0
- package/scripts/topo-online-chip-nav-test.mjs +143 -0
- package/scripts/topo-orbit-particles-retired-test.mjs +91 -0
- package/scripts/topo-panel-elevation-test.mjs +76 -0
- package/scripts/topo-panel-fadein-test.mjs +102 -0
- package/scripts/topo-panel-hot-tail-fade-test.mjs +132 -0
- package/scripts/topo-panel-hover-elevation-test.mjs +118 -0
- package/scripts/topo-panel-rx-test.mjs +90 -0
- package/scripts/topo-panel-theme-ease-test.mjs +85 -0
- package/scripts/topo-panel-title-fill-ease-test.mjs +91 -0
- package/scripts/topo-panel-title-tracking-test.mjs +78 -0
- package/scripts/topo-particle-stagger-test.mjs +82 -0
- package/scripts/topo-pin-intersection-color-ease-test.mjs +111 -0
- package/scripts/topo-pin-intersection-empty-test.mjs +129 -0
- package/scripts/topo-pin-intersection-tabular-test.mjs +96 -0
- package/scripts/topo-pin-intersection-test.mjs +121 -0
- package/scripts/topo-pin-intersection-warning-crossfade-test.mjs +107 -0
- package/scripts/topo-pin-mirror-transition-test.mjs +101 -0
- package/scripts/topo-pin-persistence-test.mjs +105 -0
- package/scripts/topo-popover-meta-test.mjs +78 -0
- package/scripts/topo-prefix-opacity-test.mjs +107 -0
- package/scripts/topo-prepublish-guard-test.mjs +95 -0
- package/scripts/topo-pressure-hover-test.mjs +123 -0
- package/scripts/topo-pressure-pin-test.mjs +119 -0
- package/scripts/topo-pressure-seg-self-hover-test.mjs +126 -0
- package/scripts/topo-pressure-seg-tooltip-test.mjs +98 -0
- package/scripts/topo-pressure-seg-transition-test.mjs +107 -0
- package/scripts/topo-pulse-dot-crossfade-test.mjs +122 -0
- package/scripts/topo-pulse-dot-retired-test.mjs +86 -0
- package/scripts/topo-recent-panel-count-freshness-test.mjs +109 -0
- package/scripts/topo-recent-panel-count-test.mjs +72 -0
- package/scripts/topo-recent-panel-count-weight-test.mjs +91 -0
- package/scripts/topo-recent-panel-footer-breath-test.mjs +142 -0
- package/scripts/topo-recent-panel-hot-header-test.mjs +132 -0
- package/scripts/topo-recent-panel-hot-tabular-test.mjs +113 -0
- package/scripts/topo-recent-panel-more-crossfade-test.mjs +124 -0
- package/scripts/topo-recent-panel-more-cyan-hover-test.mjs +151 -0
- package/scripts/topo-recent-panel-more-nav-test.mjs +127 -0
- package/scripts/topo-recent-panel-more-test.mjs +106 -0
- package/scripts/topo-recent-panel-readability-bump-test.mjs +151 -0
- package/scripts/topo-recent-row-count-fill-test.mjs +129 -0
- package/scripts/topo-recent-row-count-weight-test.mjs +112 -0
- package/scripts/topo-recent-row-fadein-test.mjs +107 -0
- package/scripts/topo-recent-row-freshness-test.mjs +102 -0
- package/scripts/topo-recent-row-hot-test.mjs +110 -0
- package/scripts/topo-recent-row-hover-lift-test.mjs +128 -0
- package/scripts/topo-recent-row-hover-test.mjs +122 -0
- package/scripts/topo-recent-row-pin-spacing-test.mjs +137 -0
- package/scripts/topo-recent-row-pin-test.mjs +126 -0
- package/scripts/topo-recent-row-tint-test.mjs +91 -0
- package/scripts/topo-recent-row-title-test.mjs +112 -0
- package/scripts/topo-recent-row-ts-alpha-test.mjs +100 -0
- package/scripts/topo-recent-row-ts-tabular-test.mjs +108 -0
- package/scripts/topo-recent-row-ts-test.mjs +95 -0
- package/scripts/topo-recent-row-typography-test.mjs +119 -0
- package/scripts/topo-recent-signal-empty-hint-test.mjs +73 -0
- package/scripts/topo-recent-signal-empty-test.mjs +63 -0
- package/scripts/topo-reduced-motion-test.mjs +77 -0
- package/scripts/topo-reset-spin-test.mjs +120 -0
- package/scripts/topo-runtime-badge-grow-test.mjs +114 -0
- package/scripts/topo-spokes-retired-test.mjs +93 -0
- package/scripts/topo-starfield-density-test.mjs +89 -0
- package/scripts/topo-status-chip-hover-test.mjs +101 -0
- package/scripts/topo-status-chip-tooltip-test.mjs +95 -0
- package/scripts/topo-statusbar-chips-self-hover-test.mjs +177 -0
- package/scripts/topo-svg-focus-test.mjs +112 -0
- package/scripts/topo-sweep-busyness-test.mjs +94 -0
- package/scripts/topo-tabular-nums-sweep-test.mjs +93 -0
- package/scripts/topo-tier-guide-dashes-test.mjs +86 -0
- package/scripts/topo-tier-ring-fadein-test.mjs +107 -0
- package/scripts/topo-tier-ring-occupancy-test.mjs +87 -0
- package/scripts/topo-tier-ring-test.mjs +69 -0
- package/scripts/topo-tier-ring-tint-test.mjs +110 -0
- package/scripts/topo-tier-stagger-test.mjs +89 -0
- package/scripts/topo-title-block-tight-leading-test.mjs +95 -0
- package/scripts/topo-title-tracking-test.mjs +98 -0
- package/scripts/topo-titleblock-gap-test.mjs +86 -0
- package/scripts/topo-titleblock-margin-test.mjs +83 -0
- package/scripts/topo-toggle-inactive-cyan-hover-test.mjs +110 -0
- package/scripts/topo-top-rail-theme-ease-test.mjs +89 -0
- package/scripts/topo-tz-ghost-test.mjs +69 -0
- package/scripts/topo-vendor-chip-hover-test.mjs +111 -0
- package/scripts/topo-vendor-chip-self-hover-test.mjs +131 -0
- package/scripts/topo-vendor-chip-tabular-test.mjs +92 -0
- package/scripts/topo-vendor-chip-threshold-test.mjs +78 -0
- package/scripts/topo-vendor-chip-weight-test.mjs +87 -0
- package/scripts/topo-vendor-count-suffix-test.mjs +95 -0
- package/scripts/topo-vendor-letter-tooltip-test.mjs +92 -0
- package/scripts/topo-vendor-pill-test.mjs +125 -0
- package/scripts/topo-vendor-pin-test.mjs +125 -0
- package/scripts/topo-view-smooth-test.mjs +131 -0
- package/scripts/topo-watermark-tracking-test.mjs +99 -0
- package/scripts/topo-working-chip-pin-test.mjs +154 -0
- package/scripts/topo-working-halo-breath-retired-test.mjs +99 -0
- package/scripts/topo-wrapper-flow-arrow-theme-test.mjs +97 -0
- package/scripts/topo-wrapper-radius-test.mjs +73 -0
- package/scripts/topo-wrapper-shadow-theme-ease-test.mjs +89 -0
- package/.next/static/chunks/0-7iowzu4.798.js +0 -1
- package/.next/static/chunks/02t5jr4shh.hy.js +0 -1
- package/.next/static/chunks/06tr6yzu4mb8_.js +0 -1
- package/.next/static/chunks/0_qfg5s5uzs7..js +0 -7
- package/.next/static/chunks/0c_yjgkf2~vd4.js +0 -1
- package/.next/static/chunks/0dev7f7k0p604.js +0 -1
- package/.next/static/chunks/0elalb9n_.~di.js +0 -1
- package/.next/static/chunks/0j_q__o8p6g83.js +0 -1
- package/.next/static/chunks/0jl6_w50zh4l5.js +0 -1
- package/.next/static/chunks/0mqwn-l7wtcdo.js +0 -7
- package/.next/static/chunks/0r4q73.-1bkyl.js +0 -1
- package/.next/static/chunks/0r_c8eccg3z2o.js +0 -1
- package/.next/static/chunks/10oi7l-tycgl9.js +0 -1
- package/.next/static/chunks/11uk1izuv3~0t.js +0 -1
- package/.next/static/chunks/13.zti9ix.1eh.css +0 -2
- /package/.next/static/{iRlmfdOMzDPfMsgsshJPP → kIvF5k1V8bBog5pvLf9QA}/_buildManifest.js +0 -0
- /package/.next/static/{iRlmfdOMzDPfMsgsshJPP → kIvF5k1V8bBog5pvLf9QA}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{iRlmfdOMzDPfMsgsshJPP → kIvF5k1V8bBog5pvLf9QA}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/* Round 84 verification: hub-halo breath amplitude + tempo reflect
|
|
2
|
+
* `workingCount`. Buckets: 0 / 1-2 / 3-5 / 6+. An idle fleet keeps
|
|
3
|
+
* the original 0.32→0.52 (light) / 0.08→0.16 (dark) cycle over 4s;
|
|
4
|
+
* a busy fleet pulses faster with a higher peak. Pure visual breath
|
|
5
|
+
* — geometry unchanged, overlap test untouched.
|
|
6
|
+
*
|
|
7
|
+
* Drive workingCount via mocked /api/hub/status sessions, then read
|
|
8
|
+
* the SMIL `<animate>` attributes off the hub halo. The bucket gets
|
|
9
|
+
* surfaced as `data-hub-busyness=0..3` for stable assertion without
|
|
10
|
+
* parsing values strings.
|
|
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 browser = await chromium.launch({ headless: true });
|
|
17
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
18
|
+
|
|
19
|
+
const probe = async (workers) => {
|
|
20
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1200 } });
|
|
21
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
22
|
+
await ctx.addInitScript(() => {
|
|
23
|
+
try {
|
|
24
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
25
|
+
localStorage.setItem('anet-topo-layout', 'ring');
|
|
26
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
27
|
+
} catch {}
|
|
28
|
+
});
|
|
29
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
30
|
+
const r = await route.fetch();
|
|
31
|
+
const b = await r.json();
|
|
32
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
33
|
+
const sessions = [];
|
|
34
|
+
for (let i = 0; i < workers; i++) {
|
|
35
|
+
sessions.push({ alias: `wkr${i}`, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
36
|
+
network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh });
|
|
37
|
+
}
|
|
38
|
+
sessions.push({ alias: 'idl', status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
39
|
+
network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh });
|
|
40
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
41
|
+
});
|
|
42
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
43
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
44
|
+
const page = await ctx.newPage();
|
|
45
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
46
|
+
await page.waitForFunction((n) => document.querySelectorAll('g[data-node]').length === n,
|
|
47
|
+
workers + 1, { timeout: 30000 });
|
|
48
|
+
await page.waitForTimeout(400);
|
|
49
|
+
|
|
50
|
+
const data = await page.evaluate(() => {
|
|
51
|
+
const halo = document.querySelector('[data-hub-busyness]');
|
|
52
|
+
const ani = halo?.querySelector('animate');
|
|
53
|
+
return {
|
|
54
|
+
busy: halo?.getAttribute('data-hub-busyness'),
|
|
55
|
+
values: ani?.getAttribute('values') || null,
|
|
56
|
+
dur: ani?.getAttribute('dur') || null,
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
await ctx.close();
|
|
60
|
+
return data;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Helper: parse "trough;peak;trough" into [trough, peak].
|
|
64
|
+
const parseValues = (v) => v ? v.split(';').map(Number) : [];
|
|
65
|
+
const peakOf = (v) => Math.max(...parseValues(v));
|
|
66
|
+
const durOf = (s) => s ? parseFloat(s.replace('s', '')) : 0;
|
|
67
|
+
|
|
68
|
+
const b0 = await probe(0); // idle only
|
|
69
|
+
const b1 = await probe(1); // 1 worker → bucket 1
|
|
70
|
+
const b2 = await probe(4); // 4 workers → bucket 2
|
|
71
|
+
const b3 = await probe(7); // 7 workers → bucket 3
|
|
72
|
+
await browser.close();
|
|
73
|
+
|
|
74
|
+
const results = {
|
|
75
|
+
b0_bucket: b0.busy === '0',
|
|
76
|
+
b1_bucket: b1.busy === '1',
|
|
77
|
+
b2_bucket: b2.busy === '2',
|
|
78
|
+
b3_bucket: b3.busy === '3',
|
|
79
|
+
// Peak climbs with bucket — dark theme values.
|
|
80
|
+
peak_monotonic: peakOf(b0.values) < peakOf(b1.values) &&
|
|
81
|
+
peakOf(b1.values) < peakOf(b2.values) &&
|
|
82
|
+
peakOf(b2.values) < peakOf(b3.values),
|
|
83
|
+
// Duration shortens with bucket.
|
|
84
|
+
dur_monotonic: durOf(b0.dur) > durOf(b1.dur) &&
|
|
85
|
+
durOf(b1.dur) > durOf(b2.dur) &&
|
|
86
|
+
durOf(b2.dur) > durOf(b3.dur),
|
|
87
|
+
// Idle baseline preserved.
|
|
88
|
+
b0_idleDur_4s: durOf(b0.dur) === 4,
|
|
89
|
+
// Busy cap respected.
|
|
90
|
+
b3_capped_24s: durOf(b3.dur) === 2.4,
|
|
91
|
+
// All have the same trough (preserving the rest baseline).
|
|
92
|
+
trough_constant: parseValues(b0.values)[0] === parseValues(b3.values)[0],
|
|
93
|
+
};
|
|
94
|
+
const ok = Object.values(results).every(Boolean);
|
|
95
|
+
console.log(`${ok ? '✅' : '❌'} hub busyness:`, JSON.stringify(results),
|
|
96
|
+
`\n b0=`, b0,
|
|
97
|
+
`\n b1=`, b1,
|
|
98
|
+
`\n b2=`, b2,
|
|
99
|
+
`\n b3=`, b3);
|
|
100
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/* Round 213 verification: hub centre crossfades the workingCount
|
|
2
|
+
* digit and the decorative highlight when count crosses zero,
|
|
3
|
+
* instead of mount/unmount snap.
|
|
4
|
+
*
|
|
5
|
+
* Pre-R213 a fleet going workingCount 0 → 1 swapped highlight
|
|
6
|
+
* circle (R130 decorative) ↔ digit text in a single frame —
|
|
7
|
+
* visible flash at the canvas's focal point. R213 always-mounts
|
|
8
|
+
* both, opacity-gates them, and adds `transition: opacity 300ms
|
|
9
|
+
* ease-out` so they crossfade. Same R181/R182/R183 always-mount
|
|
10
|
+
* pattern applied at the hub-center scope.
|
|
11
|
+
*
|
|
12
|
+
* Test scenarios (cyber dark):
|
|
13
|
+
* A. 4 working sessions (workingCount=4):
|
|
14
|
+
* · digit visible (opacity 1), data-...-visible='true'
|
|
15
|
+
* · highlight hidden (opacity 0), data-...-visible='false'
|
|
16
|
+
* · BOTH elements present in DOM (always-mount)
|
|
17
|
+
* · Both have inline transition with 'opacity 300ms'
|
|
18
|
+
* B. 4 idle sessions (workingCount=0):
|
|
19
|
+
* · digit hidden (opacity 0), data-...-visible='false'
|
|
20
|
+
* · highlight visible (opacity 0.9), data-...-visible='true'
|
|
21
|
+
* · BOTH still in DOM
|
|
22
|
+
*/
|
|
23
|
+
import { chromium } from 'playwright';
|
|
24
|
+
import { readFileSync } from 'node:fs';
|
|
25
|
+
|
|
26
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
27
|
+
const browser = await chromium.launch({ headless: true });
|
|
28
|
+
|
|
29
|
+
async function pageFor(allStatus) {
|
|
30
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
31
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
32
|
+
await ctx.addInitScript(() => {
|
|
33
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
34
|
+
});
|
|
35
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
36
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
37
|
+
const r = await route.fetch();
|
|
38
|
+
const b = await r.json();
|
|
39
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
40
|
+
const mk = (alias) => ({
|
|
41
|
+
alias, status: allStatus, model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
42
|
+
network_id: nid, project_dir: null,
|
|
43
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
44
|
+
});
|
|
45
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
46
|
+
mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
|
|
47
|
+
] } });
|
|
48
|
+
});
|
|
49
|
+
await ctx.route('**/api/hub/messages*', (r2) => r2.fulfill({ json: { messages: [] } }));
|
|
50
|
+
await ctx.route('**/api/hub/tasks*', (r2) => r2.fulfill({ json: { tasks: [] } }));
|
|
51
|
+
const page = await ctx.newPage();
|
|
52
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
53
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
54
|
+
await page.waitForTimeout(300);
|
|
55
|
+
return page;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const probe = async (page) => page.evaluate(() => {
|
|
59
|
+
const digit = document.querySelector('[data-topo-hub-working-count]');
|
|
60
|
+
const high = document.querySelector('[data-topo-hub-highlight]');
|
|
61
|
+
function px(el) {
|
|
62
|
+
if (!el) return null;
|
|
63
|
+
const cs = getComputedStyle(el);
|
|
64
|
+
return {
|
|
65
|
+
visible: el.getAttribute(el === document.querySelector('[data-topo-hub-working-count]')
|
|
66
|
+
? 'data-topo-hub-working-count-visible'
|
|
67
|
+
: 'data-topo-hub-highlight-visible'),
|
|
68
|
+
opacity: parseFloat(cs.opacity),
|
|
69
|
+
transition: el.style.transition,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
digitPresent: !!digit,
|
|
74
|
+
highlightPresent: !!high,
|
|
75
|
+
digit: px(digit),
|
|
76
|
+
highlight: px(high),
|
|
77
|
+
digitText: digit?.textContent,
|
|
78
|
+
};
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// Scenario A: all working → digit visible, highlight hidden
|
|
82
|
+
const pageA = await pageFor('working');
|
|
83
|
+
const probeA = await probe(pageA);
|
|
84
|
+
await pageA.close();
|
|
85
|
+
|
|
86
|
+
// Scenario B: all idle (workingCount=0) → highlight visible, digit hidden
|
|
87
|
+
const pageB = await pageFor('idle');
|
|
88
|
+
const probeB = await probe(pageB);
|
|
89
|
+
await pageB.close();
|
|
90
|
+
await browser.close();
|
|
91
|
+
|
|
92
|
+
const near = (got, want, tol = 0.05) => Math.abs(got - want) < tol;
|
|
93
|
+
const hasOpacityT = (s) => /opacity\s+(?:300ms|0\.3s)/.test(s || '');
|
|
94
|
+
|
|
95
|
+
const results = {
|
|
96
|
+
// A: working
|
|
97
|
+
A_digit_in_dom: probeA.digitPresent,
|
|
98
|
+
A_highlight_in_dom: probeA.highlightPresent,
|
|
99
|
+
A_digit_visible: probeA.digit?.visible === 'true',
|
|
100
|
+
A_digit_opacity_1: near(probeA.digit?.opacity ?? 0, 1),
|
|
101
|
+
A_highlight_hidden: probeA.highlight?.visible === 'false',
|
|
102
|
+
A_highlight_opacity_0: near(probeA.highlight?.opacity ?? 1, 0),
|
|
103
|
+
A_digit_text_4: probeA.digitText === '4',
|
|
104
|
+
|
|
105
|
+
// B: idle (workingCount=0)
|
|
106
|
+
B_digit_in_dom: probeB.digitPresent,
|
|
107
|
+
B_highlight_in_dom: probeB.highlightPresent,
|
|
108
|
+
B_digit_hidden: probeB.digit?.visible === 'false',
|
|
109
|
+
B_digit_opacity_0: near(probeB.digit?.opacity ?? 1, 0),
|
|
110
|
+
B_highlight_visible: probeB.highlight?.visible === 'true',
|
|
111
|
+
B_highlight_opacity_0p9: near(probeB.highlight?.opacity ?? 0, 0.9),
|
|
112
|
+
B_digit_text_0: probeB.digitText === '0',
|
|
113
|
+
|
|
114
|
+
// Both transitions present
|
|
115
|
+
A_digit_transition: hasOpacityT(probeA.digit?.transition),
|
|
116
|
+
A_highlight_transition: hasOpacityT(probeA.highlight?.transition),
|
|
117
|
+
B_digit_transition: hasOpacityT(probeB.digit?.transition),
|
|
118
|
+
B_highlight_transition: hasOpacityT(probeB.highlight?.transition),
|
|
119
|
+
};
|
|
120
|
+
const ok = Object.values(results).every(Boolean);
|
|
121
|
+
console.log(`${ok ? '✅' : '❌'} hub-center crossfade:`, JSON.stringify(results),
|
|
122
|
+
`\n probeA (working=4):`, probeA,
|
|
123
|
+
`\n probeB (working=0):`, probeB);
|
|
124
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/* Round 52 verification: clicking the central hub triggers fitView() and
|
|
2
|
+
* shows a click-ripple. The hub was previously decoration; now it's the
|
|
3
|
+
* "re-center" affordance for users who don't know the `f` shortcut.
|
|
4
|
+
*
|
|
5
|
+
* Steps:
|
|
6
|
+
* - Zoom + pan the topology to a non-default view (Cmd+= twice + drag).
|
|
7
|
+
* Confirm the zoom group's transform shifted off identity.
|
|
8
|
+
* - Click the hub. Assert:
|
|
9
|
+
* 1. the zoom group's transform returns to a fitted state (zoom and
|
|
10
|
+
* translate change), i.e. the view changed back toward identity-
|
|
11
|
+
* or-fit;
|
|
12
|
+
* 2. a transient ripple <circle> shows up at the hub center, then
|
|
13
|
+
* vanishes within ~800ms.
|
|
14
|
+
* - Confirm the hub's <title> mentions "click to fit view".
|
|
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 browser = await chromium.launch({ headless: true });
|
|
21
|
+
|
|
22
|
+
const ctx = await browser.newContext({ viewport: { width: 1280, height: 1500 } });
|
|
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
|
+
|
|
32
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
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 sessions = ['alpha', 'beta', 'gamma'].map(a => ({
|
|
38
|
+
alias: a, status: 'idle', 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 } });
|
|
42
|
+
});
|
|
43
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
44
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
45
|
+
const page = await ctx.newPage();
|
|
46
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
47
|
+
await page.waitForSelector('g[data-topo-hub]', { timeout: 30000 });
|
|
48
|
+
await page.waitForTimeout(500);
|
|
49
|
+
|
|
50
|
+
// Read the hub <title> to verify the hint text.
|
|
51
|
+
const hubTitle = await page.evaluate(() => {
|
|
52
|
+
return document.querySelector('g[data-topo-hub] title')?.textContent || '';
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Find the zoom/pan wrapper <g> (the one whose transform attribute holds
|
|
56
|
+
// the scale + translate string). The TopoGraph wraps all canvas content
|
|
57
|
+
// in a <g> with transform="translate(x, y) scale(zoom)" at the panel root.
|
|
58
|
+
// Grab the first child <g> under the SVG that carries a `transform` with
|
|
59
|
+
// a `scale(` token — that's our zoom group.
|
|
60
|
+
const readTransform = () => page.evaluate(() => {
|
|
61
|
+
const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
|
|
62
|
+
for (const g of svg.querySelectorAll(':scope > g')) {
|
|
63
|
+
const t = g.getAttribute('transform') || '';
|
|
64
|
+
if (t.includes('scale(')) return t;
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const baseTransform = await readTransform();
|
|
70
|
+
|
|
71
|
+
// Nudge zoom by simulating Ctrl+= twice via keyboard shortcut. The
|
|
72
|
+
// keymap maps '+' to zoomBy; press 'Equal' with the right modifier so
|
|
73
|
+
// browsers see '+'.
|
|
74
|
+
await page.keyboard.press('+');
|
|
75
|
+
await page.keyboard.press('+');
|
|
76
|
+
await page.keyboard.press('+');
|
|
77
|
+
await page.waitForTimeout(150);
|
|
78
|
+
const zoomedTransform = await readTransform();
|
|
79
|
+
|
|
80
|
+
// Confirm the zoom transform actually changed.
|
|
81
|
+
const zoomChanged = baseTransform !== zoomedTransform;
|
|
82
|
+
|
|
83
|
+
// Click the hub center. Locator-based click hits the union bbox edge and
|
|
84
|
+
// gets intercepted by the SVG panel rect (which must keep pointer events
|
|
85
|
+
// for canvas pan). Sample the inner core circle (r=10 at viewBox 500,330)
|
|
86
|
+
// via getScreenCTM and click that on-screen point directly.
|
|
87
|
+
const hubCenter = await page.evaluate(() => {
|
|
88
|
+
const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
|
|
89
|
+
const core = [...svg.querySelectorAll('g[data-topo-hub] circle')].find(
|
|
90
|
+
c => c.getAttribute('r') === '10'
|
|
91
|
+
);
|
|
92
|
+
if (!core) return null;
|
|
93
|
+
const ctm = core.getScreenCTM();
|
|
94
|
+
const cx = +core.getAttribute('cx');
|
|
95
|
+
const cy = +core.getAttribute('cy');
|
|
96
|
+
return { x: cx * ctm.a + cy * ctm.c + ctm.e, y: cx * ctm.b + cy * ctm.d + ctm.f };
|
|
97
|
+
});
|
|
98
|
+
if (!hubCenter) { console.log('❌ hub core circle not found'); process.exit(1); }
|
|
99
|
+
await page.mouse.click(hubCenter.x, hubCenter.y);
|
|
100
|
+
await page.waitForTimeout(150);
|
|
101
|
+
const afterClickTransform = await readTransform();
|
|
102
|
+
const ripplePresent = await page.evaluate(() => {
|
|
103
|
+
const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
|
|
104
|
+
// The click ripple is a <circle> with stroke-width="2" + an <animate>.
|
|
105
|
+
const ripples = [...svg.querySelectorAll('circle')].filter(c => {
|
|
106
|
+
const sw = c.getAttribute('stroke-width');
|
|
107
|
+
return sw === '2' && c.querySelector('animate[attributeName="r"]');
|
|
108
|
+
});
|
|
109
|
+
return ripples.length > 0;
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
await page.waitForTimeout(800);
|
|
113
|
+
const rippleGone = await page.evaluate(() => {
|
|
114
|
+
const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
|
|
115
|
+
const ripples = [...svg.querySelectorAll('circle')].filter(c => {
|
|
116
|
+
const sw = c.getAttribute('stroke-width');
|
|
117
|
+
return sw === '2' && c.querySelector('animate[attributeName="r"]');
|
|
118
|
+
});
|
|
119
|
+
return ripples.length === 0;
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
await browser.close();
|
|
123
|
+
|
|
124
|
+
const results = {
|
|
125
|
+
hubTitleHasFitHint: /click to fit view/i.test(hubTitle),
|
|
126
|
+
zoomShifted: zoomChanged,
|
|
127
|
+
hubClickRestoredView: afterClickTransform !== zoomedTransform,
|
|
128
|
+
ripplePresentDuringClick: ripplePresent,
|
|
129
|
+
rippleClearsAfter600ms: rippleGone,
|
|
130
|
+
};
|
|
131
|
+
const ok = Object.values(results).every(Boolean);
|
|
132
|
+
console.log(`${ok ? '✅' : '❌'} hub click fit:`, JSON.stringify(results),
|
|
133
|
+
`\n hubTitle=${JSON.stringify(hubTitle)}`,
|
|
134
|
+
`\n base =${baseTransform}`,
|
|
135
|
+
`\n zoomed =${zoomedTransform}`,
|
|
136
|
+
`\n fitBack =${afterClickTransform}`);
|
|
137
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/* Round 209 verification: hub workingCount digit scales 1.0 → 1.08
|
|
2
|
+
* on hub hover, matching R177's ring r 14→17 grow. Closes the
|
|
3
|
+
* "hub responds to hover" gesture across both structural pieces
|
|
4
|
+
* (ring + digit) instead of just the ring.
|
|
5
|
+
*
|
|
6
|
+
* Pre-R209 hovering the hub:
|
|
7
|
+
* ring · r 14 → 17 (R177)
|
|
8
|
+
* digit · scale 1.0 (no change — planted at centre)
|
|
9
|
+
* R209 propagates the gesture to the digit so ring + digit rise
|
|
10
|
+
* together.
|
|
11
|
+
*
|
|
12
|
+
* Test (cyber dark, 4 working sessions):
|
|
13
|
+
* 1. data-topo-hub-working-count='4' digit element present.
|
|
14
|
+
* 2. Idle: data-...-hovered='false', computed transform scale 1.0.
|
|
15
|
+
* 3. Transition includes 'transform 200ms ease-out'.
|
|
16
|
+
* 4. transform-box: fill-box + transform-origin: center are set
|
|
17
|
+
* (the SVG transform-anchor idiom shared with R184/R186).
|
|
18
|
+
* 5. Hover g[data-topo-hub] — wait past 240ms.
|
|
19
|
+
* 6. data-...-hovered='true', computed transform scale ≈ 1.08
|
|
20
|
+
* (Chrome computes scale(1.08) as matrix(1.08,0,0,1.08,0,0)
|
|
21
|
+
* or similar — extract the scale factor via the matrix).
|
|
22
|
+
* 7. Move away, wait — back to hovered='false' + scale 1.0.
|
|
23
|
+
*/
|
|
24
|
+
import { chromium } from 'playwright';
|
|
25
|
+
import { readFileSync } from 'node:fs';
|
|
26
|
+
|
|
27
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
28
|
+
const browser = await chromium.launch({ headless: true });
|
|
29
|
+
|
|
30
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
31
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
32
|
+
await ctx.addInitScript(() => {
|
|
33
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
37
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
38
|
+
const r = await route.fetch();
|
|
39
|
+
const b = await r.json();
|
|
40
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
41
|
+
const mk = (alias) => ({
|
|
42
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
43
|
+
network_id: nid, project_dir: null,
|
|
44
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
45
|
+
});
|
|
46
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
47
|
+
mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
|
|
48
|
+
] } });
|
|
49
|
+
});
|
|
50
|
+
await ctx.route('**/api/hub/messages*', (r2) => r2.fulfill({ json: { messages: [] } }));
|
|
51
|
+
await ctx.route('**/api/hub/tasks*', (r2) => r2.fulfill({ json: { tasks: [] } }));
|
|
52
|
+
|
|
53
|
+
const page = await ctx.newPage();
|
|
54
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
55
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
56
|
+
await page.waitForSelector('[data-topo-hub-working-count]', { timeout: 10000, state: 'attached' });
|
|
57
|
+
await page.waitForTimeout(300);
|
|
58
|
+
|
|
59
|
+
// Parse CSS computed transform (matrix form or none/identity) → scale x.
|
|
60
|
+
function extractScale(transform) {
|
|
61
|
+
if (!transform || transform === 'none') return 1;
|
|
62
|
+
let m = transform.match(/matrix\(\s*([\-\d.]+)/);
|
|
63
|
+
if (m) return +m[1];
|
|
64
|
+
m = transform.match(/scale\(\s*([\-\d.]+)/);
|
|
65
|
+
if (m) return +m[1];
|
|
66
|
+
return 1;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const probe = async () => page.evaluate(() => {
|
|
70
|
+
const el = document.querySelector('[data-topo-hub-working-count]');
|
|
71
|
+
if (!el) return null;
|
|
72
|
+
const cs = getComputedStyle(el);
|
|
73
|
+
return {
|
|
74
|
+
count: el.getAttribute('data-topo-hub-working-count'),
|
|
75
|
+
hovered: el.getAttribute('data-topo-hub-working-count-hovered'),
|
|
76
|
+
transform: cs.transform,
|
|
77
|
+
transformBox: cs.transformBox,
|
|
78
|
+
transformOrigin: cs.transformOrigin,
|
|
79
|
+
transition: el.style.transition,
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const idle = await probe();
|
|
84
|
+
|
|
85
|
+
await page.hover('g[data-topo-hub]');
|
|
86
|
+
await page.waitForTimeout(260);
|
|
87
|
+
const hovered = await probe();
|
|
88
|
+
|
|
89
|
+
await page.mouse.move(0, 0);
|
|
90
|
+
await page.waitForTimeout(260);
|
|
91
|
+
const settled = await probe();
|
|
92
|
+
|
|
93
|
+
await browser.close();
|
|
94
|
+
|
|
95
|
+
const idleScale = extractScale(idle?.transform);
|
|
96
|
+
const hoverScale = extractScale(hovered?.transform);
|
|
97
|
+
const settledScale = extractScale(settled?.transform);
|
|
98
|
+
|
|
99
|
+
const near = (got, want, tol = 0.03) => Math.abs(got - want) < tol;
|
|
100
|
+
|
|
101
|
+
const results = {
|
|
102
|
+
digit_present: !!idle,
|
|
103
|
+
count_4: idle?.count === '4',
|
|
104
|
+
idle_hovered_false: idle?.hovered === 'false',
|
|
105
|
+
idle_scale_1: near(idleScale, 1.0),
|
|
106
|
+
transition_has_transform: /transform\s+(?:200ms|0\.2s)/.test(idle?.transition || ''),
|
|
107
|
+
transform_box_fill_box: idle?.transformBox === 'fill-box',
|
|
108
|
+
// With transform-box: fill-box, Chrome resolves 'center' against the
|
|
109
|
+
// digit's own bbox into pixel values (e.g. '3.47826px 6.52174px' for
|
|
110
|
+
// an "4" glyph) — that's the correct fill-box anchor. Accept either
|
|
111
|
+
// keyword 'center' / '50%' OR resolved pixels (not '0 0' which would
|
|
112
|
+
// indicate fill-box didn't resolve).
|
|
113
|
+
transform_origin_anchored: /center|50%|\d+(?:\.\d+)?px\s+\d+(?:\.\d+)?px/.test(idle?.transformOrigin || '')
|
|
114
|
+
&& idle?.transformOrigin !== '0px 0px'
|
|
115
|
+
&& idle?.transformOrigin !== '0 0',
|
|
116
|
+
|
|
117
|
+
hover_hovered_true: hovered?.hovered === 'true',
|
|
118
|
+
hover_scale_1p08: near(hoverScale, 1.08),
|
|
119
|
+
hover_scale_grew: hoverScale > idleScale + 0.02,
|
|
120
|
+
|
|
121
|
+
settled_hovered_false: settled?.hovered === 'false',
|
|
122
|
+
settled_scale_back: near(settledScale, 1.0),
|
|
123
|
+
};
|
|
124
|
+
const ok = Object.values(results).every(Boolean);
|
|
125
|
+
console.log(`${ok ? '✅' : '❌'} hub digit scale:`, JSON.stringify(results),
|
|
126
|
+
`\n idle:`, idle, `\n idleScale:`, idleScale,
|
|
127
|
+
`\n hovered:`, hovered, `\n hoverScale:`, hoverScale,
|
|
128
|
+
`\n settled:`, settled, `\n settledScale:`, settledScale);
|
|
129
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/* Round 176 verification: hub joins the first-paint fade-in
|
|
2
|
+
* family as the 6th surface.
|
|
3
|
+
*
|
|
4
|
+
* Pre-R176 the hub (ring-layout center, visual anchor) popped
|
|
5
|
+
* in instantly while R174 tier rings staggered around it 0-120ms,
|
|
6
|
+
* R9 nodes emanated 0-540ms, R172 edges chased 280-980ms. The
|
|
7
|
+
* focal element wasn't part of the wave it was supposed to be
|
|
8
|
+
* leading.
|
|
9
|
+
*
|
|
10
|
+
* R176 adds .anet-fade-in to the hub <g> at delay 0 — establishes
|
|
11
|
+
* the hub as the canvas-center anchor the tier wave grows from.
|
|
12
|
+
* Composes cleanly with the existing anet-topo-svg-focus class
|
|
13
|
+
* (R159 keyboard focus ring).
|
|
14
|
+
*
|
|
15
|
+
* Test:
|
|
16
|
+
* 1. Force ring layout
|
|
17
|
+
* 2. Mock 3 sessions
|
|
18
|
+
* 3. Probe [data-topo-hub] element
|
|
19
|
+
* 4. Assert className includes BOTH anet-topo-svg-focus AND
|
|
20
|
+
* anet-fade-in (composition preserved)
|
|
21
|
+
* 5. Assert data-topo-hub-fade-delay=0
|
|
22
|
+
* 6. R159 a11y attrs (role=button, aria-label) still intact
|
|
23
|
+
*/
|
|
24
|
+
import { chromium } from 'playwright';
|
|
25
|
+
import { readFileSync } from 'node:fs';
|
|
26
|
+
|
|
27
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
28
|
+
const browser = await chromium.launch({ headless: true });
|
|
29
|
+
|
|
30
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
31
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
32
|
+
await ctx.addInitScript(() => {
|
|
33
|
+
try {
|
|
34
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
35
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
36
|
+
localStorage.setItem('anet-topo-layout', 'ring');
|
|
37
|
+
} catch {}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
41
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
42
|
+
const r = await route.fetch();
|
|
43
|
+
const b = await r.json();
|
|
44
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
45
|
+
const mk = (alias) => ({
|
|
46
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
47
|
+
network_id: nid, project_dir: null,
|
|
48
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
49
|
+
});
|
|
50
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta'), mk('gamma')] } });
|
|
51
|
+
});
|
|
52
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
53
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
54
|
+
|
|
55
|
+
const page = await ctx.newPage();
|
|
56
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
57
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
|
|
58
|
+
await page.waitForSelector('[data-topo-hub]', { timeout: 10000 });
|
|
59
|
+
await page.waitForTimeout(400);
|
|
60
|
+
|
|
61
|
+
const probe = await page.evaluate(() => {
|
|
62
|
+
const el = document.querySelector('[data-topo-hub]');
|
|
63
|
+
if (!el) return null;
|
|
64
|
+
return {
|
|
65
|
+
className: el.getAttribute('class') || '',
|
|
66
|
+
fadeDelayAttr: parseFloat(el.getAttribute('data-topo-hub-fade-delay') || ''),
|
|
67
|
+
role: el.getAttribute('role'),
|
|
68
|
+
tabIndex: el.getAttribute('tabindex'),
|
|
69
|
+
ariaLabel: el.getAttribute('aria-label') || '',
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
await browser.close();
|
|
74
|
+
|
|
75
|
+
const results = {
|
|
76
|
+
hub_found: probe !== null,
|
|
77
|
+
has_focus_class: probe?.className.includes('anet-topo-svg-focus'),
|
|
78
|
+
has_fade_class: probe?.className.includes('anet-fade-in'),
|
|
79
|
+
fade_delay_attr_0: probe?.fadeDelayAttr === 0,
|
|
80
|
+
// R159 a11y preservation
|
|
81
|
+
r159_role_button: probe?.role === 'button',
|
|
82
|
+
r159_tabIndex_0: probe?.tabIndex === '0',
|
|
83
|
+
r159_aria_label_present: /Network hub/.test(probe?.ariaLabel || ''),
|
|
84
|
+
};
|
|
85
|
+
const ok = Object.values(results).every(Boolean);
|
|
86
|
+
console.log(`${ok ? '✅' : '❌'} hub fade-in:`, JSON.stringify(results),
|
|
87
|
+
`\n hub =`, probe);
|
|
88
|
+
process.exit(ok ? 0 : 1);
|