@sleep2agi/agent-network-dashboard 0.4.6 → 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 +107 -0
- package/app/components/HelpOverlay.tsx +109 -1
- package/app/components/ServersDrawer.tsx +181 -5
- package/app/components/TopoGraph.tsx +6409 -286
- package/app/globals.css +110 -5
- 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-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-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-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-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 +13 -5
- 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-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-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 +7 -2
- 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-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-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-count-unit-test.mjs +112 -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-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-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-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/088ouqy7tekty.js +0 -2
- package/.next/static/chunks/0hyl7mdapsz38.js +0 -1
- package/.next/static/chunks/0mqwn-l7wtcdo.js +0 -7
- package/.next/static/chunks/127kw7~wsnyu7.css +0 -2
- package/.next/static/chunks/16yfxp9ak2gdm.js +0 -1
- /package/.next/static/{23MxP0RBVA4BaOJ_HMYmM → 424zX3i80AJ_wHF_otKuP}/_buildManifest.js +0 -0
- /package/.next/static/{23MxP0RBVA4BaOJ_HMYmM → 424zX3i80AJ_wHF_otKuP}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{23MxP0RBVA4BaOJ_HMYmM → 424zX3i80AJ_wHF_otKuP}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/* Round 86 verification: hovering a group label previews the same
|
|
2
|
+
* dim mechanism as hovering a member node. R63 wired click-to-pin
|
|
3
|
+
* but forgot the hover preview — same R83 hover/click consistency
|
|
4
|
+
* gap. Now closed for group labels too.
|
|
5
|
+
*
|
|
6
|
+
* Fleet: 3 alpha + 2 beta in grid layout. Hover alpha label →
|
|
7
|
+
* alpha nodes stay bright, beta nodes dim. Hover beta label →
|
|
8
|
+
* inverse. Click alpha label → pin (R63 existing). Hover beta
|
|
9
|
+
* while alpha pinned → live transient preview of beta, alpha dims.
|
|
10
|
+
* Leave beta label → alpha pin restores (activeGroup =
|
|
11
|
+
* hoveredGroup ?? pinnedGroup).
|
|
12
|
+
*/
|
|
13
|
+
import { chromium } from 'playwright';
|
|
14
|
+
import { readFileSync } from 'node:fs';
|
|
15
|
+
|
|
16
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
17
|
+
const browser = await chromium.launch({ headless: true });
|
|
18
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
19
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
20
|
+
await ctx.addInitScript(() => {
|
|
21
|
+
try {
|
|
22
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
23
|
+
localStorage.setItem('anet-topo-layout', 'grid');
|
|
24
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
25
|
+
sessionStorage.removeItem('anet-topo-pinned-status');
|
|
26
|
+
sessionStorage.removeItem('anet-topo-pinned-group');
|
|
27
|
+
} catch {}
|
|
28
|
+
});
|
|
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) => ({
|
|
36
|
+
alias, status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
37
|
+
network_id: nid, project_dir: null,
|
|
38
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
39
|
+
});
|
|
40
|
+
const sessions = [mk('alpha1'), mk('alpha2'), mk('alpha3'), mk('beta1'), mk('beta2')];
|
|
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.waitForFunction(() => document.querySelectorAll('[data-group-label-hit]').length >= 2, { timeout: 30000 });
|
|
48
|
+
await page.waitForTimeout(400);
|
|
49
|
+
|
|
50
|
+
const ops = () => page.evaluate(() => {
|
|
51
|
+
const o = {};
|
|
52
|
+
for (const a of ['alpha1', 'alpha2', 'alpha3', 'beta1', 'beta2']) {
|
|
53
|
+
const g = document.querySelector(`g[data-node="${a}"]`);
|
|
54
|
+
o[a] = g ? +(g.style.opacity || '1') : null;
|
|
55
|
+
}
|
|
56
|
+
return o;
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const before = await ops();
|
|
60
|
+
|
|
61
|
+
// Hover the alpha label — alphas should stay bright, betas dim.
|
|
62
|
+
await page.locator('[data-group-label-hit="alpha"]').hover();
|
|
63
|
+
await page.waitForTimeout(250);
|
|
64
|
+
const onHoverAlpha = await ops();
|
|
65
|
+
|
|
66
|
+
// Leave (move mouse far away) — baseline restores.
|
|
67
|
+
await page.mouse.move(10, 10);
|
|
68
|
+
await page.waitForTimeout(250);
|
|
69
|
+
const afterLeaveAlpha = await ops();
|
|
70
|
+
|
|
71
|
+
// Hover beta label — inverse.
|
|
72
|
+
await page.locator('[data-group-label-hit="beta"]').hover();
|
|
73
|
+
await page.waitForTimeout(250);
|
|
74
|
+
const onHoverBeta = await ops();
|
|
75
|
+
|
|
76
|
+
await page.mouse.move(10, 10);
|
|
77
|
+
await page.waitForTimeout(250);
|
|
78
|
+
|
|
79
|
+
// Click alpha label to pin → alpha pinned (R63 behaviour preserved).
|
|
80
|
+
await page.locator('[data-group-label-hit="alpha"]').click();
|
|
81
|
+
await page.mouse.move(10, 10);
|
|
82
|
+
await page.waitForTimeout(250);
|
|
83
|
+
const afterPinAlpha = await ops();
|
|
84
|
+
|
|
85
|
+
// Hover beta while alpha is pinned — transient preview wins.
|
|
86
|
+
await page.locator('[data-group-label-hit="beta"]').hover();
|
|
87
|
+
await page.waitForTimeout(250);
|
|
88
|
+
const onHoverBetaWhilePin = await ops();
|
|
89
|
+
|
|
90
|
+
// Leave beta — alpha pin restores.
|
|
91
|
+
await page.mouse.move(10, 10);
|
|
92
|
+
await page.waitForTimeout(250);
|
|
93
|
+
const afterLeaveBetaWhilePin = await ops();
|
|
94
|
+
|
|
95
|
+
await browser.close();
|
|
96
|
+
|
|
97
|
+
const bright = (v) => v != null && v >= 0.55;
|
|
98
|
+
const dim = (v) => v != null && v < 0.4;
|
|
99
|
+
const results = {
|
|
100
|
+
before_baseline: bright(before.alpha1) && bright(before.beta1),
|
|
101
|
+
hoverAlpha_alphasBright: bright(onHoverAlpha.alpha1) && bright(onHoverAlpha.alpha2) && bright(onHoverAlpha.alpha3),
|
|
102
|
+
hoverAlpha_betasDim: dim(onHoverAlpha.beta1) && dim(onHoverAlpha.beta2),
|
|
103
|
+
leaveAlpha_restores: bright(afterLeaveAlpha.alpha1) && bright(afterLeaveAlpha.beta1),
|
|
104
|
+
hoverBeta_betasBright: bright(onHoverBeta.beta1) && bright(onHoverBeta.beta2),
|
|
105
|
+
hoverBeta_alphasDim: dim(onHoverBeta.alpha1) && dim(onHoverBeta.alpha2),
|
|
106
|
+
pinAlpha_keepsAlpha: bright(afterPinAlpha.alpha1) && bright(afterPinAlpha.alpha2),
|
|
107
|
+
pinAlpha_dimsBeta: dim(afterPinAlpha.beta1) && dim(afterPinAlpha.beta2),
|
|
108
|
+
hoverBetaPin_transient: bright(onHoverBetaWhilePin.beta1) && dim(onHoverBetaWhilePin.alpha1),
|
|
109
|
+
leaveBetaPin_alphaRestored: bright(afterLeaveBetaWhilePin.alpha1) && dim(afterLeaveBetaWhilePin.beta1),
|
|
110
|
+
};
|
|
111
|
+
const ok = Object.values(results).every(Boolean);
|
|
112
|
+
console.log(`${ok ? '✅' : '❌'} group-label hover:`, JSON.stringify(results),
|
|
113
|
+
`\n before=`, before,
|
|
114
|
+
`\n onHoverAlpha=`, onHoverAlpha,
|
|
115
|
+
`\n onHoverBeta=`, onHoverBeta,
|
|
116
|
+
`\n afterPinAlpha=`, afterPinAlpha,
|
|
117
|
+
`\n onHoverBetaPin=`, onHoverBetaWhilePin,
|
|
118
|
+
`\n leaveBetaPin=`, afterLeaveBetaWhilePin);
|
|
119
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/* Round 218 verification: group label text gains letter-spacing
|
|
2
|
+
* transition on pin — 0px → 0.5px when group is pinned. Hover and
|
|
3
|
+
* pin share fill colour (R63 legendHeadline) so pre-R218 they
|
|
4
|
+
* looked typographically identical. R218 spreads the pinned text
|
|
5
|
+
* to read "locked in" at the text level.
|
|
6
|
+
*
|
|
7
|
+
* Test (cyber dark, 17 sessions in 2 prefix groups → 'group-A' +
|
|
8
|
+
* 'group-B' groupBoxes after switch to grid layout):
|
|
9
|
+
* 1. Switch to grid layout to trigger groupBoxes rendering.
|
|
10
|
+
* 2. Both group labels render with data-group-label.
|
|
11
|
+
* 3. Idle: data-group-label-pinned='false', computed
|
|
12
|
+
* letter-spacing ~ 0px.
|
|
13
|
+
* 4. Inline transition includes 'letter-spacing 200ms'.
|
|
14
|
+
* 5. Click one group label → that group becomes pinned.
|
|
15
|
+
* 6. Pinned label: data-group-label-pinned='true', computed
|
|
16
|
+
* letter-spacing ~ 0.5px.
|
|
17
|
+
* 7. Other (un-pinned) group stays at 0px letter-spacing.
|
|
18
|
+
* 8. Click pinned again → unpin → back to 0px.
|
|
19
|
+
*/
|
|
20
|
+
import { chromium } from 'playwright';
|
|
21
|
+
import { readFileSync } from 'node:fs';
|
|
22
|
+
|
|
23
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
24
|
+
const browser = await chromium.launch({ headless: true });
|
|
25
|
+
|
|
26
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
27
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
28
|
+
await ctx.addInitScript(() => {
|
|
29
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } 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 mk = (alias) => ({
|
|
38
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
39
|
+
network_id: nid, project_dir: null,
|
|
40
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
41
|
+
});
|
|
42
|
+
// Two prefix groups: 'alpha-*' and 'beta-*' (each ≥2 → forms a group)
|
|
43
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
44
|
+
mk('alpha-1'), mk('alpha-2'), mk('alpha-3'),
|
|
45
|
+
mk('beta-1'), mk('beta-2'), mk('beta-3'),
|
|
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 === 6, { timeout: 30000 });
|
|
54
|
+
await page.waitForSelector('[data-topo-chrome-layout="grid"]', { timeout: 10000 });
|
|
55
|
+
await page.waitForTimeout(200);
|
|
56
|
+
|
|
57
|
+
// Switch to grid layout to trigger groupBoxes
|
|
58
|
+
await page.click('[data-topo-chrome-layout="grid"]');
|
|
59
|
+
await page.waitForTimeout(600);
|
|
60
|
+
await page.waitForSelector('[data-group-label]', { timeout: 5000, state: 'attached' });
|
|
61
|
+
await page.waitForTimeout(300);
|
|
62
|
+
|
|
63
|
+
const probe = async () => page.evaluate(() => {
|
|
64
|
+
return [...document.querySelectorAll('[data-group-label]')].map(el => {
|
|
65
|
+
const cs = getComputedStyle(el);
|
|
66
|
+
return {
|
|
67
|
+
key: el.getAttribute('data-group-label'),
|
|
68
|
+
pinned: el.getAttribute('data-group-label-pinned'),
|
|
69
|
+
spacing: cs.letterSpacing,
|
|
70
|
+
transition: el.style.transition,
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const idle = await probe();
|
|
76
|
+
|
|
77
|
+
// Click first group label to pin it. The label hit area is the
|
|
78
|
+
// invisible rect — but the <text> itself often gets the click via
|
|
79
|
+
// the wrapper <g> with onClick. Find the parent <g role='button'>
|
|
80
|
+
// (data-group-label-hit) and click it.
|
|
81
|
+
const firstKey = idle[0]?.key;
|
|
82
|
+
if (firstKey) {
|
|
83
|
+
await page.click(`[data-group-label-hit="${firstKey}"]`);
|
|
84
|
+
await page.waitForTimeout(260); // past 200ms transition
|
|
85
|
+
}
|
|
86
|
+
const pinned = await probe();
|
|
87
|
+
|
|
88
|
+
// Click again to unpin
|
|
89
|
+
if (firstKey) {
|
|
90
|
+
await page.click(`[data-group-label-hit="${firstKey}"]`);
|
|
91
|
+
await page.waitForTimeout(260);
|
|
92
|
+
}
|
|
93
|
+
const unpinned = await probe();
|
|
94
|
+
|
|
95
|
+
await browser.close();
|
|
96
|
+
|
|
97
|
+
// CSS resolves `letterSpacing: '0px'` to the keyword 'normal' in
|
|
98
|
+
// computed style — they're equivalent. Accept both forms as "zero
|
|
99
|
+
// spacing"; numeric values still parse cleanly via parseFloat.
|
|
100
|
+
const near = (s, want, tol = 0.15) => {
|
|
101
|
+
if (want === 0 && (s === 'normal' || s === '0px' || s === '0' || !s)) return true;
|
|
102
|
+
const got = parseFloat(s || '0');
|
|
103
|
+
return Math.abs(got - want) < tol;
|
|
104
|
+
};
|
|
105
|
+
const hasLetterSpacingT = (s) => /letter-spacing\s+(?:200ms|0\.2s)/.test(s || '');
|
|
106
|
+
|
|
107
|
+
const firstIdle = idle[0];
|
|
108
|
+
const firstPinned = pinned.find(p => p.key === firstKey);
|
|
109
|
+
const otherIdle = idle[1];
|
|
110
|
+
const otherPinned = pinned.find(p => p.key !== firstKey);
|
|
111
|
+
const firstUnpin = unpinned.find(p => p.key === firstKey);
|
|
112
|
+
|
|
113
|
+
const results = {
|
|
114
|
+
two_groups_present: idle.length >= 2,
|
|
115
|
+
|
|
116
|
+
// Idle: all unpinned, spacing 0, transition present
|
|
117
|
+
all_idle_unpinned: idle.every(p => p.pinned === 'false'),
|
|
118
|
+
all_idle_spacing_zero: idle.every(p => near(p.spacing, 0)),
|
|
119
|
+
all_have_transition: idle.every(p => hasLetterSpacingT(p.transition)),
|
|
120
|
+
|
|
121
|
+
// First pinned, second still idle
|
|
122
|
+
first_pinned_attr: firstPinned?.pinned === 'true',
|
|
123
|
+
first_pinned_spacing: near(firstPinned?.spacing, 0.5),
|
|
124
|
+
other_stays_unpinned: otherPinned?.pinned === 'false',
|
|
125
|
+
other_spacing_unchanged: near(otherPinned?.spacing, 0),
|
|
126
|
+
|
|
127
|
+
// After unpin: spacing back to 0
|
|
128
|
+
unpinned_attr_false: firstUnpin?.pinned === 'false',
|
|
129
|
+
unpinned_spacing_zero: near(firstUnpin?.spacing, 0),
|
|
130
|
+
};
|
|
131
|
+
const ok = Object.values(results).every(Boolean);
|
|
132
|
+
console.log(`${ok ? '✅' : '❌'} group-label pin spacing:`, JSON.stringify(results),
|
|
133
|
+
`\n idle:`, idle,
|
|
134
|
+
`\n pinned:`, pinned,
|
|
135
|
+
`\n unpinned:`, unpinned);
|
|
136
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/* Round 107 verification: group-label hit `<rect>` picks up a tint
|
|
2
|
+
* on hover and pin — extends the R104 (recent-signal) / R105
|
|
3
|
+
* (legend) list-item-tint pattern to the SVG group labels. The tint
|
|
4
|
+
* is pal.legendAccent (cyan) since groups don't carry an inherent
|
|
5
|
+
* swatch; same colour family R68 already uses for hovered/pinned
|
|
6
|
+
* group-box strokes.
|
|
7
|
+
*
|
|
8
|
+
* Verifies idle / hover / pinned via the new data-group-label-tinted
|
|
9
|
+
* attribute + fill/opacity. Pin opacity stronger than hover.
|
|
10
|
+
*/
|
|
11
|
+
import { chromium } from 'playwright';
|
|
12
|
+
import { readFileSync } from 'node:fs';
|
|
13
|
+
|
|
14
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
15
|
+
const browser = await chromium.launch({ headless: true });
|
|
16
|
+
|
|
17
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
18
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
19
|
+
await ctx.addInitScript(() => {
|
|
20
|
+
try {
|
|
21
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
22
|
+
localStorage.setItem('anet-topo-layout', 'grid');
|
|
23
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
24
|
+
sessionStorage.removeItem('anet-topo-pinned-group');
|
|
25
|
+
} catch {}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
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 mk = (alias) => ({
|
|
34
|
+
alias, status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
35
|
+
network_id: nid, project_dir: null,
|
|
36
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
37
|
+
});
|
|
38
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
39
|
+
mk('alpha1'), mk('alpha2'), mk('alpha3'),
|
|
40
|
+
mk('beta1'), mk('beta2'),
|
|
41
|
+
] } });
|
|
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.waitForFunction(() => document.querySelectorAll('[data-group-label-hit]').length >= 2, { timeout: 30000 });
|
|
48
|
+
await page.waitForTimeout(400);
|
|
49
|
+
|
|
50
|
+
const readLabel = (key) => page.evaluate((k) => {
|
|
51
|
+
const g = document.querySelector(`[data-group-label-hit="${k}"]`);
|
|
52
|
+
if (!g) return null;
|
|
53
|
+
const rect = g.querySelector('rect');
|
|
54
|
+
return {
|
|
55
|
+
tinted: rect?.getAttribute('data-group-label-tinted') || '',
|
|
56
|
+
fill: rect?.getAttribute('fill') || '',
|
|
57
|
+
opacity: rect?.getAttribute('opacity') || '',
|
|
58
|
+
};
|
|
59
|
+
}, key);
|
|
60
|
+
|
|
61
|
+
const beforeAlpha = await readLabel('alpha');
|
|
62
|
+
const beforeBeta = await readLabel('beta');
|
|
63
|
+
|
|
64
|
+
// Hover alpha label.
|
|
65
|
+
await page.locator('[data-group-label-hit="alpha"]').hover();
|
|
66
|
+
await page.waitForTimeout(250);
|
|
67
|
+
const onHoverAlpha = await readLabel('alpha');
|
|
68
|
+
const onHoverBeta = await readLabel('beta');
|
|
69
|
+
|
|
70
|
+
// Move pointer far away.
|
|
71
|
+
await page.mouse.move(10, 10);
|
|
72
|
+
await page.waitForTimeout(250);
|
|
73
|
+
|
|
74
|
+
// Pin beta.
|
|
75
|
+
await page.locator('[data-group-label-hit="beta"]').click();
|
|
76
|
+
await page.mouse.move(10, 10);
|
|
77
|
+
await page.waitForTimeout(250);
|
|
78
|
+
const pinBeta = await readLabel('beta');
|
|
79
|
+
const pinAlphaUnchanged = await readLabel('alpha');
|
|
80
|
+
|
|
81
|
+
await browser.close();
|
|
82
|
+
|
|
83
|
+
const opacity = (s) => parseFloat(s) || 0;
|
|
84
|
+
const results = {
|
|
85
|
+
before_alphaTransparent: beforeAlpha?.tinted === 'none' && beforeAlpha?.fill === 'transparent',
|
|
86
|
+
before_betaTransparent: beforeBeta?.tinted === 'none' && beforeBeta?.fill === 'transparent',
|
|
87
|
+
hover_alphaTinted: onHoverAlpha?.tinted === 'hover' && onHoverAlpha?.fill !== 'transparent',
|
|
88
|
+
hover_alphaOpacityLow: opacity(onHoverAlpha?.opacity || '') > 0 && opacity(onHoverAlpha?.opacity || '') < 0.2,
|
|
89
|
+
hover_betaUnchanged: onHoverBeta?.tinted === 'none',
|
|
90
|
+
pin_betaPinned: pinBeta?.tinted === 'pinned' && pinBeta?.fill !== 'transparent',
|
|
91
|
+
pin_strongerThanHover: opacity(pinBeta?.opacity || '') > opacity(onHoverAlpha?.opacity || ''),
|
|
92
|
+
pin_alphaClean: pinAlphaUnchanged?.tinted === 'none',
|
|
93
|
+
};
|
|
94
|
+
const ok = Object.values(results).every(Boolean);
|
|
95
|
+
console.log(`${ok ? '✅' : '❌'} group-label tint:`, JSON.stringify(results),
|
|
96
|
+
`\n beforeAlpha=`, beforeAlpha,
|
|
97
|
+
`\n onHoverAlpha=`, onHoverAlpha,
|
|
98
|
+
`\n pinBeta=`, pinBeta);
|
|
99
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -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,119 @@
|
|
|
1
|
+
/* Round 63 verification: clicking the group label pins the group focus
|
|
2
|
+
* via pinnedGroup. activeGroup = hoveredGroup ?? pinnedGroup is read by
|
|
3
|
+
* the R8 inFocus dim mechanic and the R53 in-group edge boost.
|
|
4
|
+
*
|
|
5
|
+
* - Click a group label → that group's nodes stay bright, others dim
|
|
6
|
+
* via the existing inFocus formula (opacity 0.32).
|
|
7
|
+
* - Move mouse away → pin persists (transient hover cleared).
|
|
8
|
+
* - In-group edge between OTHER team members brightens 1.3× even
|
|
9
|
+
* without an alias hover.
|
|
10
|
+
* - Esc clears the pin (R62 extended).
|
|
11
|
+
* - aria-pressed flips on the group <text>.
|
|
12
|
+
*/
|
|
13
|
+
import { chromium } from 'playwright';
|
|
14
|
+
import { readFileSync } from 'node:fs';
|
|
15
|
+
|
|
16
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
17
|
+
const browser = await chromium.launch({ headless: true });
|
|
18
|
+
|
|
19
|
+
const ctx = await browser.newContext({ viewport: { width: 1280, height: 1500 } });
|
|
20
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
21
|
+
await ctx.addInitScript(() => {
|
|
22
|
+
try {
|
|
23
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
24
|
+
localStorage.setItem('anet-topo-layout', 'grid');
|
|
25
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
26
|
+
} catch {}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
30
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
31
|
+
const r = await route.fetch();
|
|
32
|
+
const b = await r.json();
|
|
33
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
34
|
+
const sessions = [
|
|
35
|
+
{ alias: 'alpha1', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
36
|
+
{ alias: 'alpha2', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
37
|
+
{ alias: 'alpha3', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
38
|
+
{ alias: 'beta', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
39
|
+
];
|
|
40
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
41
|
+
});
|
|
42
|
+
const now = new Date().toISOString();
|
|
43
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
|
|
44
|
+
// alpha2 → alpha3: both in alpha group, neither is the pinned-group's "exact alias"
|
|
45
|
+
{ from_alias: 'alpha2', to_alias: 'alpha3', content: 'm', created_at: now },
|
|
46
|
+
// alpha1 → beta: alpha1 in pinned group, beta outside → should dim
|
|
47
|
+
{ from_alias: 'alpha1', to_alias: 'beta', content: 'm', created_at: now },
|
|
48
|
+
] } }));
|
|
49
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
50
|
+
const page = await ctx.newPage();
|
|
51
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
52
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
53
|
+
await page.waitForTimeout(600);
|
|
54
|
+
|
|
55
|
+
const nodeOpacities = () => page.evaluate(() => {
|
|
56
|
+
const o = {};
|
|
57
|
+
for (const a of ['alpha1', 'alpha2', 'alpha3', 'beta']) {
|
|
58
|
+
const g = document.querySelector(`g[data-node="${a}"]`);
|
|
59
|
+
o[a] = g ? +(g.style.opacity || '1') : null;
|
|
60
|
+
}
|
|
61
|
+
return o;
|
|
62
|
+
});
|
|
63
|
+
const edgeOpacities = () => page.evaluate(() => {
|
|
64
|
+
const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
|
|
65
|
+
const out = {};
|
|
66
|
+
for (const g of svg.querySelectorAll(':scope > g > g')) {
|
|
67
|
+
const t = g.querySelector('path[data-edge-hitbox] title');
|
|
68
|
+
if (!t) continue;
|
|
69
|
+
const route = (t.textContent || '').split('\n')[0];
|
|
70
|
+
const base = [...g.querySelectorAll(':scope > path')].find(
|
|
71
|
+
p => !p.hasAttribute('data-edge-hitbox') && p.hasAttribute('marker-end')
|
|
72
|
+
);
|
|
73
|
+
if (base) out[route] = +base.getAttribute('opacity');
|
|
74
|
+
}
|
|
75
|
+
return out;
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const before = await nodeOpacities();
|
|
79
|
+
const beforeEdges = await edgeOpacities();
|
|
80
|
+
|
|
81
|
+
// Click the alpha group label via its R63 hitbox wrapper.
|
|
82
|
+
const alphaLabel = page.locator('g[data-group-label-hit="alpha"]').first();
|
|
83
|
+
const exists = await alphaLabel.count() > 0;
|
|
84
|
+
if (!exists) { console.log('❌ alpha group label hit not found'); process.exit(1); }
|
|
85
|
+
await alphaLabel.click({ force: true });
|
|
86
|
+
// Mouse away so we observe pin-only state.
|
|
87
|
+
await page.mouse.move(10, 10);
|
|
88
|
+
await page.waitForTimeout(300);
|
|
89
|
+
const afterPinNodes = await nodeOpacities();
|
|
90
|
+
const afterPinEdges = await edgeOpacities();
|
|
91
|
+
const ariaPressed = await page.evaluate(() => document.querySelector('g[data-group-label-hit="alpha"]')?.getAttribute('aria-pressed'));
|
|
92
|
+
|
|
93
|
+
// Esc to release.
|
|
94
|
+
await page.evaluate(() => document.activeElement && typeof document.activeElement.blur === 'function' && document.activeElement.blur());
|
|
95
|
+
await page.keyboard.press('Escape');
|
|
96
|
+
await page.waitForTimeout(300);
|
|
97
|
+
const afterEscNodes = await nodeOpacities();
|
|
98
|
+
const ariaAfterEsc = await page.evaluate(() => document.querySelector('g[data-group-label-hit="alpha"]')?.getAttribute('aria-pressed'));
|
|
99
|
+
|
|
100
|
+
await browser.close();
|
|
101
|
+
|
|
102
|
+
const bright = (v) => v != null && v >= 0.55;
|
|
103
|
+
const dim = (v) => v != null && v < 0.4;
|
|
104
|
+
const results = {
|
|
105
|
+
before_baseline: bright(before.alpha1) && bright(before.alpha2) && bright(before.alpha3) && bright(before.beta),
|
|
106
|
+
pin_keepsAlphaTeam: bright(afterPinNodes.alpha1) && bright(afterPinNodes.alpha2) && bright(afterPinNodes.alpha3),
|
|
107
|
+
pin_dimsBeta: dim(afterPinNodes.beta),
|
|
108
|
+
pin_ariaPressed: ariaPressed === 'true',
|
|
109
|
+
pin_inGroupEdgeBoosts: afterPinEdges['alpha2 → alpha3'] > beforeEdges['alpha2 → alpha3'] * 1.15,
|
|
110
|
+
pin_leavingEdgeDims: afterPinEdges['alpha1 → beta'] < beforeEdges['alpha1 → beta'] * 0.6,
|
|
111
|
+
esc_releasesPin: bright(afterEscNodes.alpha1) && bright(afterEscNodes.beta),
|
|
112
|
+
esc_ariaBackToFalse: ariaAfterEsc === 'false',
|
|
113
|
+
};
|
|
114
|
+
const ok = Object.values(results).every(Boolean);
|
|
115
|
+
console.log(`${ok ? '✅' : '❌'} group pin:`, JSON.stringify(results),
|
|
116
|
+
`\n before nodes=`, before, ` edges=`, beforeEdges,
|
|
117
|
+
`\n afterPin nodes=`, afterPinNodes, ` edges=`, afterPinEdges, ` aria=${ariaPressed}`,
|
|
118
|
+
`\n afterEsc nodes=`, afterEscNodes, ` aria=${ariaAfterEsc}`);
|
|
119
|
+
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);
|