@sleep2agi/agent-network-dashboard 0.4.6-preview.99 → 0.5.0
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 +31 -31
- 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/agent-network-dashboard_09kk21a._.js +4 -2
- 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/server/server-reference-manifest.js +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/0aauz~36q5n2a.css +2 -0
- package/.next/static/chunks/0f~0kgy2pycyc.js +1 -0
- package/.next/static/chunks/0l_~q07bhpkcx.js +7 -0
- package/.next/static/chunks/1175-dp18a1e_.js +1 -0
- package/.next/static/chunks/1552j0415oeal.js +4 -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/CommandPalette.tsx +41 -1
- package/app/components/HelpOverlay.tsx +55 -0
- package/app/components/ServersDrawer.tsx +181 -5
- package/app/components/TopoGraph.tsx +5514 -336
- package/app/globals.css +93 -6
- 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-links-chip-nav-test.mjs +117 -0
- package/scripts/topo-active-links-chip-self-hover-test.mjs +171 -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-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-chip-unit-demotion-test.mjs +93 -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-vendor-pin-test.mjs +103 -0
- package/scripts/topo-corner-radius-unify-test.mjs +103 -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-fadein-test.mjs +106 -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-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-filter-pill-aliases-test.mjs +121 -0
- package/scripts/topo-filter-pill-count-tabular-test.mjs +135 -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-label-a11y-test.mjs +113 -0
- package/scripts/topo-group-label-count-inherit-test.mjs +99 -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-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-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-march-busyness-test.mjs +155 -0
- 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-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-center-crossfade-test.mjs +124 -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-working-count-test.mjs +99 -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-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-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-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-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-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-count-unit-test.mjs +112 -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-prefix-opacity-test.mjs +107 -0
- package/scripts/topo-prepublish-guard-test.mjs +95 -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-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-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-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-tint-test.mjs +110 -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-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-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/03w4rgx_vt8ec.js +0 -1
- package/.next/static/chunks/072so8rfx2tt-.css +0 -2
- package/.next/static/chunks/0o.k-.0kl4bz5.js +0 -2
- package/.next/static/chunks/121d8yvfl8l_..js +0 -7
- package/.next/static/chunks/148o1e4ijgwp1.js +0 -1
- package/scripts/topo-arrival-ping-test.mjs +0 -87
- package/scripts/topo-dispatch-pulse-test.mjs +0 -83
- /package/.next/static/{Dhky2WTh8mvJhw56taHVs → 424zX3i80AJ_wHF_otKuP}/_buildManifest.js +0 -0
- /package/.next/static/{Dhky2WTh8mvJhw56taHVs → 424zX3i80AJ_wHF_otKuP}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{Dhky2WTh8mvJhw56taHVs → 424zX3i80AJ_wHF_otKuP}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/* Round 99 verification: group label <title> tooltip lists members
|
|
2
|
+
* + status breakdown. Same R97/R98 pattern — anywhere the UI shows
|
|
3
|
+
* "alpha · 3" should hover-explain WHICH 3.
|
|
4
|
+
*
|
|
5
|
+
* Fleet: alpha group has 3 members (2 working + 1 idle), beta has
|
|
6
|
+
* 2 idle. Pin alpha to confirm the title's last line flips from
|
|
7
|
+
* "click to pin" → "click to release pin".
|
|
8
|
+
*/
|
|
9
|
+
import { chromium } from 'playwright';
|
|
10
|
+
import { readFileSync } from 'node:fs';
|
|
11
|
+
|
|
12
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
13
|
+
const browser = await chromium.launch({ headless: true });
|
|
14
|
+
|
|
15
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
16
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
17
|
+
await ctx.addInitScript(() => {
|
|
18
|
+
try {
|
|
19
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
20
|
+
localStorage.setItem('anet-topo-layout', 'grid');
|
|
21
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
22
|
+
sessionStorage.removeItem('anet-topo-pinned-group');
|
|
23
|
+
} catch {}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
27
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
28
|
+
const r = await route.fetch();
|
|
29
|
+
const b = await r.json();
|
|
30
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
31
|
+
const mk = (alias, status) => ({
|
|
32
|
+
alias, status, model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
33
|
+
network_id: nid, project_dir: null,
|
|
34
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
35
|
+
});
|
|
36
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
37
|
+
mk('alpha1', 'working'),
|
|
38
|
+
mk('alpha2', 'working'),
|
|
39
|
+
mk('alpha3', 'idle'),
|
|
40
|
+
mk('beta1', 'idle'),
|
|
41
|
+
mk('beta2', 'idle'),
|
|
42
|
+
] } });
|
|
43
|
+
});
|
|
44
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
45
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
46
|
+
const page = await ctx.newPage();
|
|
47
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
48
|
+
await page.waitForFunction(() => document.querySelectorAll('[data-group-label-hit]').length >= 2, { timeout: 30000 });
|
|
49
|
+
await page.waitForTimeout(400);
|
|
50
|
+
|
|
51
|
+
const readTitle = (groupKey) => page.evaluate((k) => {
|
|
52
|
+
const g = document.querySelector(`[data-group-label-hit="${k}"]`);
|
|
53
|
+
return g?.querySelector('title')?.textContent || '';
|
|
54
|
+
}, groupKey);
|
|
55
|
+
|
|
56
|
+
const alphaTitle = await readTitle('alpha');
|
|
57
|
+
const betaTitle = await readTitle('beta');
|
|
58
|
+
|
|
59
|
+
// Pin alpha — last line should flip.
|
|
60
|
+
await page.locator('[data-group-label-hit="alpha"]').click();
|
|
61
|
+
await page.waitForTimeout(250);
|
|
62
|
+
const alphaTitlePinned = await readTitle('alpha');
|
|
63
|
+
|
|
64
|
+
await browser.close();
|
|
65
|
+
|
|
66
|
+
const results = {
|
|
67
|
+
alpha_heading: /^alpha \(3 members\)/m.test(alphaTitle),
|
|
68
|
+
alpha_statusSummary: /2 working · 1 idle/.test(alphaTitle),
|
|
69
|
+
alpha_listsMembers: /alpha1.*alpha2.*alpha3/.test(alphaTitle),
|
|
70
|
+
alpha_pinHint: /click to pin this group/.test(alphaTitle),
|
|
71
|
+
beta_heading: /^beta \(2 members\)/m.test(betaTitle),
|
|
72
|
+
beta_statusSummary: /2 idle/.test(betaTitle),
|
|
73
|
+
beta_noWorkingInSummary: !/working/.test(betaTitle),
|
|
74
|
+
beta_listsMembers: /beta1.*beta2/.test(betaTitle),
|
|
75
|
+
pinned_hintFlipped: /click to release pin/.test(alphaTitlePinned),
|
|
76
|
+
pinned_keepsMembers: /alpha1.*alpha2.*alpha3/.test(alphaTitlePinned),
|
|
77
|
+
};
|
|
78
|
+
const ok = Object.values(results).every(Boolean);
|
|
79
|
+
console.log(`${ok ? '✅' : '❌'} group-label title:`, JSON.stringify(results),
|
|
80
|
+
`\n alpha:\n${alphaTitle}`,
|
|
81
|
+
`\n beta:\n${betaTitle}`,
|
|
82
|
+
`\n alphaPinned:\n${alphaTitlePinned}`);
|
|
83
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/* Round 207 verification: group box label status pip tspans
|
|
2
|
+
* (`2w`, `1i`, `1o`) get anet-fade-in on mount. Pre-R207 a tier
|
|
3
|
+
* crossing 0 → 1+ snap-popped its tspan into the group label;
|
|
4
|
+
* R207 eases the entrance the same way R203 did for recent rows.
|
|
5
|
+
*
|
|
6
|
+
* Group boxes only render in grid layout when sessions share a
|
|
7
|
+
* ≥2-char alias prefix (computeGroups union-find).
|
|
8
|
+
*
|
|
9
|
+
* Test:
|
|
10
|
+
* Mock 4 sessions: alpha-1 working, alpha-2 idle, beta-1 working,
|
|
11
|
+
* beta-2 working. Two groups form:
|
|
12
|
+
* alpha · 1 working + 1 idle (both pips fire)
|
|
13
|
+
* beta · 2 working (only working pip fires)
|
|
14
|
+
* Switch to grid layout via the R163 toggle, wait for group boxes.
|
|
15
|
+
* Probe:
|
|
16
|
+
* 1. >=3 [data-group-pip] elements present (alpha working +
|
|
17
|
+
* alpha idle + beta working)
|
|
18
|
+
* 2. No [data-group-pip="offline"] (offline tier is empty)
|
|
19
|
+
* 3. Every pip's className includes 'anet-fade-in'
|
|
20
|
+
* 4. CSS animationName resolves to 'anet-fade-in'
|
|
21
|
+
* 5. tspan textContent matches digit+letter pattern (e.g.
|
|
22
|
+
* '2w', '1i')
|
|
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, status) => ({
|
|
42
|
+
alias, status, model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
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
|
+
// R319 fixture: both groups must be multi-tier so all tier pips
|
|
48
|
+
// render. Pre-R319 beta was 2 working (single-tier) and its pip
|
|
49
|
+
// rendered fine; post-R319 a tier pip is dropped when its count
|
|
50
|
+
// equals box.count, so beta needs idle to remain multi-tier here.
|
|
51
|
+
mk('alpha-1', 'working'),
|
|
52
|
+
mk('alpha-2', 'idle'),
|
|
53
|
+
mk('beta-1', 'working'),
|
|
54
|
+
mk('beta-2', 'idle'),
|
|
55
|
+
] } });
|
|
56
|
+
});
|
|
57
|
+
await ctx.route('**/api/hub/messages*', (r2) => r2.fulfill({ json: { messages: [] } }));
|
|
58
|
+
await ctx.route('**/api/hub/tasks*', (r2) => r2.fulfill({ json: { tasks: [] } }));
|
|
59
|
+
|
|
60
|
+
const page = await ctx.newPage();
|
|
61
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
62
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
63
|
+
await page.waitForSelector('[data-topo-chrome-layout="grid"]', { timeout: 10000 });
|
|
64
|
+
await page.waitForTimeout(200);
|
|
65
|
+
|
|
66
|
+
// Switch to grid layout to trigger groupBoxes rendering
|
|
67
|
+
await page.click('[data-topo-chrome-layout="grid"]');
|
|
68
|
+
await page.waitForTimeout(600); // R170 layout-switch crossfade + group derivation
|
|
69
|
+
await page.waitForSelector('[data-group-pip]', { timeout: 5000 });
|
|
70
|
+
await page.waitForTimeout(300);
|
|
71
|
+
|
|
72
|
+
const pips = await page.evaluate(() => {
|
|
73
|
+
return [...document.querySelectorAll('[data-group-pip]')].map(el => {
|
|
74
|
+
const cs = getComputedStyle(el);
|
|
75
|
+
return {
|
|
76
|
+
tier: el.getAttribute('data-group-pip'),
|
|
77
|
+
text: el.textContent,
|
|
78
|
+
className: el.getAttribute('class') || '',
|
|
79
|
+
animationName: cs.animationName,
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
await browser.close();
|
|
85
|
+
|
|
86
|
+
const working = pips.filter(p => p.tier === 'working');
|
|
87
|
+
const idleP = pips.filter(p => p.tier === 'idle');
|
|
88
|
+
const offline = pips.filter(p => p.tier === 'offline');
|
|
89
|
+
|
|
90
|
+
const has = (s, c) => (s || '').split(/\s+/).includes(c);
|
|
91
|
+
|
|
92
|
+
const results = {
|
|
93
|
+
at_least_three_pips: pips.length >= 3,
|
|
94
|
+
has_working_pip: working.length >= 2, // alpha + beta both have working
|
|
95
|
+
has_idle_pip: idleP.length >= 1, // alpha has 1 idle
|
|
96
|
+
no_offline_pip: offline.length === 0,
|
|
97
|
+
all_have_fadein_class: pips.every(p => has(p.className, 'anet-fade-in')),
|
|
98
|
+
all_have_animation_bound: pips.every(p => /anet-fade-in/i.test(p.animationName)),
|
|
99
|
+
text_format_correct: pips.every(p => /^\d+[wio]$/.test(p.text || '')),
|
|
100
|
+
};
|
|
101
|
+
const ok = Object.values(results).every(Boolean);
|
|
102
|
+
console.log(`${ok ? '✅' : '❌'} group pip fade-in:`, JSON.stringify(results),
|
|
103
|
+
`\n pips:`, pips);
|
|
104
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/* Round 319 verification: prefix-group label drops the redundant
|
|
2
|
+
* tier pip when its count equals box.count (single-tier groups).
|
|
3
|
+
*
|
|
4
|
+
* Vincent telegram 5304 (2026-05-16) flagged real-data screenshot
|
|
5
|
+
* with `ai-insight · 6 6i`, `blueleap · 3 3i`, `P站 · 4 4i` as
|
|
6
|
+
* 比较难看 — the same number was visually doubled (total chip "·N"
|
|
7
|
+
* + tier pip "Ni") whenever a group was single-tier.
|
|
8
|
+
*
|
|
9
|
+
* R319: tier pip is now conditionally rendered with both
|
|
10
|
+
* `box.statuses.{tier} > 0` AND `box.statuses.{tier} !== box.count`
|
|
11
|
+
* so single-tier groups collapse to `· N` (no duplicated digit).
|
|
12
|
+
* Multi-tier groups keep all pips — the breakdown is genuinely
|
|
13
|
+
* additional info there.
|
|
14
|
+
*
|
|
15
|
+
* Fixture:
|
|
16
|
+
* - alpha-1..alpha-3: all working (single-tier, count=3, working=3)
|
|
17
|
+
* - beta-1, beta-2: working; beta-3, beta-4: idle (multi-tier)
|
|
18
|
+
*
|
|
19
|
+
* Contract:
|
|
20
|
+
* - alpha group: no [data-group-pip="working"] tspan (dropped because
|
|
21
|
+
* working === count)
|
|
22
|
+
* - alpha group: still has [data-group-label-count] with value=3
|
|
23
|
+
* (R229 total chip untouched)
|
|
24
|
+
* - beta group: has [data-group-pip="working"] AND [data-group-pip="idle"]
|
|
25
|
+
* (multi-tier — both pips render because neither equals count)
|
|
26
|
+
* - beta group: [data-group-label-count-value]=4 still rendered
|
|
27
|
+
* - R317 inactive Layout toggle gray-400 + R318 active font-medium
|
|
28
|
+
* + R294 pulse absent all preserved.
|
|
29
|
+
*/
|
|
30
|
+
import { chromium } from 'playwright';
|
|
31
|
+
import { readFileSync } from 'node:fs';
|
|
32
|
+
|
|
33
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
34
|
+
const browser = await chromium.launch({ headless: true });
|
|
35
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
36
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
37
|
+
await ctx.addInitScript(() => {
|
|
38
|
+
try {
|
|
39
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
40
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
41
|
+
localStorage.setItem('anet-topo-layout', 'grid');
|
|
42
|
+
} catch {}
|
|
43
|
+
});
|
|
44
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
45
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
46
|
+
const r = await route.fetch();
|
|
47
|
+
const b = await r.json();
|
|
48
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
49
|
+
const mk = (alias, status) => ({
|
|
50
|
+
alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
51
|
+
network_id: nid, project_dir: null,
|
|
52
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
53
|
+
});
|
|
54
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
55
|
+
// alpha — single-tier (all working, count=3)
|
|
56
|
+
mk('alpha-1', 'working'),
|
|
57
|
+
mk('alpha-2', 'working'),
|
|
58
|
+
mk('alpha-3', 'working'),
|
|
59
|
+
// beta — multi-tier (2 working + 2 idle, count=4)
|
|
60
|
+
mk('beta-1', 'working'),
|
|
61
|
+
mk('beta-2', 'working'),
|
|
62
|
+
mk('beta-3', 'idle'),
|
|
63
|
+
mk('beta-4', 'idle'),
|
|
64
|
+
] } });
|
|
65
|
+
});
|
|
66
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
67
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
68
|
+
|
|
69
|
+
const page = await ctx.newPage();
|
|
70
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
71
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 7, { timeout: 30000 });
|
|
72
|
+
await page.waitForSelector('[data-group-label-count]', { timeout: 10000, state: 'attached' });
|
|
73
|
+
await page.waitForTimeout(500);
|
|
74
|
+
|
|
75
|
+
const probe = await page.evaluate(() => {
|
|
76
|
+
// Find each group's <text data-group-label> wrapper and its pip
|
|
77
|
+
// children + count tspan.
|
|
78
|
+
const labels = Array.from(document.querySelectorAll('[data-group-label]'));
|
|
79
|
+
const byGroup = {};
|
|
80
|
+
for (const label of labels) {
|
|
81
|
+
const key = label.getAttribute('data-group-label');
|
|
82
|
+
const countTspan = label.querySelector('[data-group-label-count]');
|
|
83
|
+
const pips = Array.from(label.querySelectorAll('[data-group-pip]'));
|
|
84
|
+
byGroup[key] = {
|
|
85
|
+
hasCountTspan: !!countTspan,
|
|
86
|
+
countValue: countTspan?.getAttribute('data-group-label-count-value') ?? null,
|
|
87
|
+
pipTiers: pips.map(p => p.getAttribute('data-group-pip')),
|
|
88
|
+
pipTexts: pips.map(p => p.textContent),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
groups: byGroup,
|
|
93
|
+
r317InactiveLayoutCls:
|
|
94
|
+
document.querySelector('[data-topo-chrome-layout="grid"]')?.className ?? '',
|
|
95
|
+
r317ActiveLayoutCls:
|
|
96
|
+
document.querySelector('[data-topo-chrome-layout="ring"]')?.className ?? '',
|
|
97
|
+
pulseCount: document.querySelectorAll('[data-pulse-wrapper]').length,
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
await browser.close();
|
|
101
|
+
|
|
102
|
+
// R106 cluster algorithm preserves the trailing separator in the
|
|
103
|
+
// group key (e.g. alpha-1/2/3 → key 'alpha-'); match by prefix.
|
|
104
|
+
const alpha = probe.groups['alpha-'] || probe.groups['alpha'] || null;
|
|
105
|
+
const beta = probe.groups['beta-'] || probe.groups['beta'] || null;
|
|
106
|
+
|
|
107
|
+
const results = {
|
|
108
|
+
alpha_group_found: alpha !== null,
|
|
109
|
+
alpha_has_count_tspan: alpha?.hasCountTspan === true,
|
|
110
|
+
alpha_count_value_3: alpha?.countValue === '3',
|
|
111
|
+
alpha_no_working_pip: !alpha?.pipTiers.includes('working'),
|
|
112
|
+
alpha_no_pips_at_all: alpha?.pipTiers.length === 0,
|
|
113
|
+
beta_group_found: beta !== null,
|
|
114
|
+
beta_has_count_tspan: beta?.hasCountTspan === true,
|
|
115
|
+
beta_count_value_4: beta?.countValue === '4',
|
|
116
|
+
beta_has_working_pip: beta?.pipTiers.includes('working'),
|
|
117
|
+
beta_has_idle_pip: beta?.pipTiers.includes('idle'),
|
|
118
|
+
beta_no_offline_pip: !beta?.pipTiers.includes('offline'),
|
|
119
|
+
// R317 regression — Layout toggle inactive Grid is the layout-active
|
|
120
|
+
// here (we forced grid via initScript), so check that Ring is the
|
|
121
|
+
// INACTIVE one with text-gray-400. After R318, the ACTIVE button
|
|
122
|
+
// (grid) has font-medium.
|
|
123
|
+
r317_ring_inactive_gray_400: probe.r317ActiveLayoutCls.includes('text-gray-400'),
|
|
124
|
+
r318_grid_active_font_medium: probe.r317InactiveLayoutCls.includes('font-medium'),
|
|
125
|
+
r294_pulse_absent: probe.pulseCount === 0,
|
|
126
|
+
};
|
|
127
|
+
const ok = Object.values(results).every(Boolean);
|
|
128
|
+
console.log(`${ok ? '✅' : '❌'} group-pip single-tier drop:`, JSON.stringify(results),
|
|
129
|
+
'\n alpha:', alpha,
|
|
130
|
+
'\n beta:', beta);
|
|
131
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/* Round 230 verification: R58 status mix pip strip tspans
|
|
2
|
+
* ([data-group-pip='working|idle|offline']) get fontVariantNumeric:
|
|
3
|
+
* 'tabular-nums' so the digit doesn't jitter the dx-offset pip
|
|
4
|
+
* chain at the 9→10 boundary.
|
|
5
|
+
*
|
|
6
|
+
* Scenario: 1 group with mixed tiers — alpha-1/2 working, alpha-3
|
|
7
|
+
* idle, alpha-4 offline. R106 cluster forms one alpha- group with
|
|
8
|
+
* box.statuses.working=2, idle=1, offline=1 → all three pips
|
|
9
|
+
* present.
|
|
10
|
+
*
|
|
11
|
+
* Verifies each pip:
|
|
12
|
+
* getComputedStyle.fontVariantNumeric contains 'tabular-nums'
|
|
13
|
+
* text matches the expected '{N}{tier-letter}' shape
|
|
14
|
+
* fill is tier-specific (color check — sanity)
|
|
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
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
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
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
27
|
+
localStorage.setItem('anet-topo-layout', 'grid');
|
|
28
|
+
} catch {}
|
|
29
|
+
});
|
|
30
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
31
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
32
|
+
const r = await route.fetch();
|
|
33
|
+
const b = await r.json();
|
|
34
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
35
|
+
const mk = (alias, status) => ({
|
|
36
|
+
alias, status,
|
|
37
|
+
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: [
|
|
42
|
+
mk('alpha-1', 'working'),
|
|
43
|
+
mk('alpha-2', 'working'),
|
|
44
|
+
mk('alpha-3', 'idle'),
|
|
45
|
+
mk('alpha-4', 'offline'),
|
|
46
|
+
] } });
|
|
47
|
+
});
|
|
48
|
+
await ctx.route('**/api/hub/messages*', (r2) => r2.fulfill({ json: { messages: [] } }));
|
|
49
|
+
await ctx.route('**/api/hub/tasks*', (r2) => r2.fulfill({ json: { tasks: [] } }));
|
|
50
|
+
|
|
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.waitForSelector('[data-group-pip="working"]', { timeout: 10000, state: 'attached' });
|
|
55
|
+
await page.waitForTimeout(400);
|
|
56
|
+
|
|
57
|
+
const out = await page.evaluate(() => {
|
|
58
|
+
const grab = (tier) => {
|
|
59
|
+
const el = document.querySelector(`[data-group-pip="${tier}"]`);
|
|
60
|
+
if (!el) return null;
|
|
61
|
+
return {
|
|
62
|
+
tier,
|
|
63
|
+
text: el.textContent,
|
|
64
|
+
fontVarNumeric: getComputedStyle(el).fontVariantNumeric,
|
|
65
|
+
fill: getComputedStyle(el).fill,
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
return {
|
|
69
|
+
working: grab('working'),
|
|
70
|
+
idle: grab('idle'),
|
|
71
|
+
offline: grab('offline'),
|
|
72
|
+
};
|
|
73
|
+
});
|
|
74
|
+
await browser.close();
|
|
75
|
+
|
|
76
|
+
const hasTab = (s) => /tabular-nums/.test(s || '');
|
|
77
|
+
|
|
78
|
+
const results = {
|
|
79
|
+
working_present: out.working !== null,
|
|
80
|
+
working_text: out.working?.text === '2w',
|
|
81
|
+
working_tabular: hasTab(out.working?.fontVarNumeric),
|
|
82
|
+
idle_present: out.idle !== null,
|
|
83
|
+
idle_text: out.idle?.text === '1i',
|
|
84
|
+
idle_tabular: hasTab(out.idle?.fontVarNumeric),
|
|
85
|
+
offline_present: out.offline !== null,
|
|
86
|
+
offline_text: out.offline?.text === '1o',
|
|
87
|
+
offline_tabular: hasTab(out.offline?.fontVarNumeric),
|
|
88
|
+
// Sanity: tier-specific fills still distinct from each other
|
|
89
|
+
fills_distinct: out.working && out.idle && out.offline &&
|
|
90
|
+
new Set([out.working.fill, out.idle.fill, out.offline.fill]).size === 3,
|
|
91
|
+
};
|
|
92
|
+
const ok = Object.values(results).every(Boolean);
|
|
93
|
+
console.log(`${ok ? '✅' : '❌'} group-pip tabular:`, JSON.stringify(results),
|
|
94
|
+
'\n pips:', out);
|
|
95
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/* Round 173 verification: group boxes (grid layout) fade-in
|
|
2
|
+
* alongside the R9/R172 first-paint wave.
|
|
3
|
+
*
|
|
4
|
+
* Pre-R173 the structural box frames appeared instantly while
|
|
5
|
+
* nodes inside eased in via R9 (0-540ms) and edges via R172
|
|
6
|
+
* (280-980ms). The reveal felt like 'frame slams down, nodes
|
|
7
|
+
* drift in'.
|
|
8
|
+
*
|
|
9
|
+
* R173 wraps each group box <g> in:
|
|
10
|
+
* className="transition-opacity anet-fade-in"
|
|
11
|
+
* style={{ animationDelay: 'boxIdx × 60ms (cap 8)' }}
|
|
12
|
+
* data-group-fade-delay={boxIdx × 60}
|
|
13
|
+
*
|
|
14
|
+
* Test:
|
|
15
|
+
* 1. Force grid layout
|
|
16
|
+
* 2. Mock 4 prefix-clustered sessions → 2 groups
|
|
17
|
+
* 3. Probe each [data-group] wrapper
|
|
18
|
+
* 4. Assert anet-fade-in className present
|
|
19
|
+
* 5. Assert animation-delay 0ms, 60ms
|
|
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
|
+
const browser = await chromium.launch({ headless: true });
|
|
26
|
+
|
|
27
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
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
|
+
localStorage.setItem('anet-topo-layout', 'grid');
|
|
34
|
+
} catch {}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
38
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
39
|
+
const r = await route.fetch();
|
|
40
|
+
const b = await r.json();
|
|
41
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
42
|
+
const mk = (alias) => ({
|
|
43
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
44
|
+
network_id: nid, project_dir: null,
|
|
45
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
46
|
+
});
|
|
47
|
+
// 2 distinct prefix groups, 2 sessions each → 2 group boxes
|
|
48
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
49
|
+
mk('agents-a1'), mk('agents-a2'),
|
|
50
|
+
mk('infra-b1'), mk('infra-b2'),
|
|
51
|
+
] } });
|
|
52
|
+
});
|
|
53
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
54
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
55
|
+
|
|
56
|
+
const page = await ctx.newPage();
|
|
57
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
58
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
59
|
+
await page.waitForSelector('[data-group]', { timeout: 10000 });
|
|
60
|
+
await page.waitForTimeout(400);
|
|
61
|
+
|
|
62
|
+
const probe = await page.evaluate(() => {
|
|
63
|
+
const boxes = [...document.querySelectorAll('g[data-group]')];
|
|
64
|
+
return boxes.map(b => ({
|
|
65
|
+
key: b.getAttribute('data-group'),
|
|
66
|
+
hasFadeClass: (b.getAttribute('class') || '').includes('anet-fade-in'),
|
|
67
|
+
fadeDelayAttr: parseFloat(b.getAttribute('data-group-fade-delay') || ''),
|
|
68
|
+
animationDelay: b.style.animationDelay || getComputedStyle(b).animationDelay,
|
|
69
|
+
}));
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
await browser.close();
|
|
73
|
+
|
|
74
|
+
const parseDelay = (s) => {
|
|
75
|
+
if (!s) return null;
|
|
76
|
+
if (s.endsWith('ms')) return parseFloat(s);
|
|
77
|
+
if (s.endsWith('s')) return parseFloat(s) * 1000;
|
|
78
|
+
return parseFloat(s);
|
|
79
|
+
};
|
|
80
|
+
const delays = probe.map(p => parseDelay(p.animationDelay));
|
|
81
|
+
|
|
82
|
+
const results = {
|
|
83
|
+
two_boxes_found: probe.length === 2,
|
|
84
|
+
all_have_fade_class: probe.every(p => p.hasFadeClass),
|
|
85
|
+
box0_delay_attr_0: probe[0]?.fadeDelayAttr === 0,
|
|
86
|
+
box1_delay_attr_60: probe[1]?.fadeDelayAttr === 60,
|
|
87
|
+
box0_anim_delay_0ms: delays[0] === 0,
|
|
88
|
+
box1_anim_delay_60ms: Math.abs(delays[1] - 60) < 0.5,
|
|
89
|
+
delays_strictly_increase: delays[0] < delays[1],
|
|
90
|
+
};
|
|
91
|
+
const ok = Object.values(results).every(Boolean);
|
|
92
|
+
console.log(`${ok ? '✅' : '❌'} groupbox fade-in:`, JSON.stringify(results),
|
|
93
|
+
`\n boxes =`, probe,
|
|
94
|
+
`\n delays (ms) =`, delays);
|
|
95
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/* Round 142 verification: group boxes (R85+R68) gain a drop-
|
|
2
|
+
* shadow lift on hover or pin, mirroring R135's overlay panel
|
|
3
|
+
* hover-elevation. Closes the "interactive surface elevates"
|
|
4
|
+
* vocabulary across canvas-side major surfaces:
|
|
5
|
+
* R18 KPI cards (Overview)
|
|
6
|
+
* R135 overlay panels (recent-signal + legend)
|
|
7
|
+
* R142 group boxes (canvas-level filter)
|
|
8
|
+
*
|
|
9
|
+
* Each carries a soft outward shadow when hovered/selected.
|
|
10
|
+
*
|
|
11
|
+
* States walked (grid layout, three prefix groups):
|
|
12
|
+
* 1. Idle → all 3 boxes: data-group-box-lifted="false", no filter
|
|
13
|
+
* 2. Hover infra label → infra lifted=true, filter set; others stay flat
|
|
14
|
+
* 3. Leave hover → infra restored to flat
|
|
15
|
+
* 4. Pin agents via CustomEvent → agents lifted=true (R63 pin path)
|
|
16
|
+
* 5. Esc → agents back to flat
|
|
17
|
+
*/
|
|
18
|
+
import { chromium } from 'playwright';
|
|
19
|
+
import { readFileSync } from 'node:fs';
|
|
20
|
+
|
|
21
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
22
|
+
const browser = await chromium.launch({ headless: true });
|
|
23
|
+
|
|
24
|
+
const ctx = await browser.newContext({ viewport: { width: 1600, height: 1500 } });
|
|
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
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
30
|
+
// R85 group boxes only render in grid layout
|
|
31
|
+
localStorage.setItem('anet-topo-layout', 'grid');
|
|
32
|
+
} catch {}
|
|
33
|
+
});
|
|
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, status) => ({
|
|
41
|
+
alias, status, model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
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('agents-a1', 'working'), mk('agents-a2', 'idle'), mk('agents-a3', 'idle'),
|
|
47
|
+
mk('infra-b1', 'working'), mk('infra-b2', 'idle'),
|
|
48
|
+
mk('build-c1', 'idle'), mk('build-c2', 'idle'),
|
|
49
|
+
] } });
|
|
50
|
+
});
|
|
51
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
52
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
53
|
+
|
|
54
|
+
const page = await ctx.newPage();
|
|
55
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
56
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 7, { timeout: 30000 });
|
|
57
|
+
await page.waitForSelector('[data-group]', { timeout: 10000 });
|
|
58
|
+
await page.waitForTimeout(500);
|
|
59
|
+
|
|
60
|
+
const inspectAll = () => page.evaluate(() => {
|
|
61
|
+
const boxes = [...document.querySelectorAll('rect[data-group-box-pinned]')];
|
|
62
|
+
return boxes.map(rect => {
|
|
63
|
+
const parentG = rect.closest('g[data-group]');
|
|
64
|
+
return {
|
|
65
|
+
group: parentG?.getAttribute('data-group'),
|
|
66
|
+
lifted: rect.getAttribute('data-group-box-lifted'),
|
|
67
|
+
filter: rect.getAttribute('filter'),
|
|
68
|
+
pinned: rect.getAttribute('data-group-box-pinned'),
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const state1 = await inspectAll();
|
|
74
|
+
const idleAll = state1.every(b => b.lifted === 'false' && !b.filter);
|
|
75
|
+
|
|
76
|
+
// Hover infra label
|
|
77
|
+
const infra = state1.find(b => (b.group || '').startsWith('infra'));
|
|
78
|
+
await page.locator(`[data-group-label-hit="${infra.group}"]`).first().hover({ timeout: 3000 });
|
|
79
|
+
await page.waitForTimeout(250);
|
|
80
|
+
const state2 = await inspectAll();
|
|
81
|
+
const infra2 = state2.find(b => b.group === infra.group);
|
|
82
|
+
const othersFlat2 = state2.filter(b => b.group !== infra.group).every(b => b.lifted === 'false' && !b.filter);
|
|
83
|
+
|
|
84
|
+
// Mouse away → restore
|
|
85
|
+
await page.mouse.move(20, 20);
|
|
86
|
+
await page.waitForTimeout(250);
|
|
87
|
+
const state3 = await inspectAll();
|
|
88
|
+
const infra3 = state3.find(b => b.group === infra.group);
|
|
89
|
+
|
|
90
|
+
// Pin agents via CustomEvent
|
|
91
|
+
const agents = state1.find(b => (b.group || '').startsWith('agents'));
|
|
92
|
+
await page.evaluate((key) => {
|
|
93
|
+
window.dispatchEvent(new CustomEvent('anet:topo-pin', { detail: { kind: 'group', value: key } }));
|
|
94
|
+
}, agents.group);
|
|
95
|
+
await page.waitForTimeout(250);
|
|
96
|
+
const state4 = await inspectAll();
|
|
97
|
+
const agents4 = state4.find(b => b.group === agents.group);
|
|
98
|
+
|
|
99
|
+
// Esc clears pin
|
|
100
|
+
await page.keyboard.press('Escape');
|
|
101
|
+
await page.waitForTimeout(250);
|
|
102
|
+
const state5 = await inspectAll();
|
|
103
|
+
const agents5 = state5.find(b => b.group === agents.group);
|
|
104
|
+
|
|
105
|
+
await browser.close();
|
|
106
|
+
|
|
107
|
+
const liftFilter = 'url(#topo-groupbox-lift)';
|
|
108
|
+
const results = {
|
|
109
|
+
s1_idleAllFlat: idleAll,
|
|
110
|
+
|
|
111
|
+
s2_infraLifted: infra2?.lifted === 'true',
|
|
112
|
+
s2_infraHasFilter: infra2?.filter === liftFilter,
|
|
113
|
+
s2_othersStillFlat: othersFlat2,
|
|
114
|
+
|
|
115
|
+
s3_infraRestored: infra3?.lifted === 'false' && !infra3.filter,
|
|
116
|
+
|
|
117
|
+
s4_agentsLifted: agents4?.lifted === 'true',
|
|
118
|
+
s4_agentsHasFilter: agents4?.filter === liftFilter,
|
|
119
|
+
s4_agentsPinnedAttr: agents4?.pinned === 'true',
|
|
120
|
+
|
|
121
|
+
s5_agentsCleared: agents5?.lifted === 'false' && !agents5.filter,
|
|
122
|
+
};
|
|
123
|
+
const ok = Object.values(results).every(Boolean);
|
|
124
|
+
console.log(`${ok ? '✅' : '❌'} groupbox hover-lift:`, JSON.stringify(results),
|
|
125
|
+
`\n s1=`, state1,
|
|
126
|
+
`\n infra2=`, infra2, ` agents4=`, agents4, ` agents5=`, agents5);
|
|
127
|
+
process.exit(ok ? 0 : 1);
|