@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,121 @@
|
|
|
1
|
+
/* Round 239 verification: legend count text fill picks up the
|
|
2
|
+
* row's tier colour on hover/pin, completing the hover-deepen-own-
|
|
3
|
+
* hue idiom for the legend row triple (swatch + label + count).
|
|
4
|
+
*
|
|
5
|
+
* Pre-R239 the count digit had opacity 0.65→0.95 on hover but
|
|
6
|
+
* stayed at pal.legendText (neutral gray) for fill. The swatch
|
|
7
|
+
* (R197 grow) and label (R55 brighten) both reacted to hover with
|
|
8
|
+
* tier-identity signals; only the count stayed gray. R239 adds
|
|
9
|
+
* fill transition to row.fill (green/teal/slate) on hover so the
|
|
10
|
+
* row reads as one tier-coloured unit.
|
|
11
|
+
*
|
|
12
|
+
* Empty tiers (row.count === 0) stay at pal.legendText regardless
|
|
13
|
+
* of hover — empty doesn't get to claim tier identity.
|
|
14
|
+
*
|
|
15
|
+
* Test scope:
|
|
16
|
+
* - rest state: count fill === neutral (pal.legendText)
|
|
17
|
+
* - hover state (Playwright .hover() on the legend row <g>):
|
|
18
|
+
* count fill === row.fill (tier colour)
|
|
19
|
+
* - data-legend-count-fill attr flips 'neutral' ↔ 'tier'
|
|
20
|
+
* - transition list includes both opacity AND fill at 150ms
|
|
21
|
+
*
|
|
22
|
+
* Scenario: 4 working agents → working tier has count=4 (populated),
|
|
23
|
+
* idle + offline tiers stay at 0 (empty, never tier-coloured).
|
|
24
|
+
* Hover the working legend row, verify only that row's count
|
|
25
|
+
* flips to tier colour.
|
|
26
|
+
*/
|
|
27
|
+
import { chromium } from 'playwright';
|
|
28
|
+
import { readFileSync } from 'node:fs';
|
|
29
|
+
|
|
30
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
31
|
+
const browser = await chromium.launch({ headless: true });
|
|
32
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
33
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
34
|
+
await ctx.addInitScript(() => {
|
|
35
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
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: 'claude-code-cli',
|
|
44
|
+
network_id: nid, project_dir: null,
|
|
45
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
46
|
+
});
|
|
47
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
48
|
+
mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
|
|
49
|
+
] } });
|
|
50
|
+
});
|
|
51
|
+
await ctx.route('**/api/hub/messages*', (r2) => r2.fulfill({ json: { messages: [] } }));
|
|
52
|
+
await ctx.route('**/api/hub/tasks*', (r2) => r2.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 === 4, { timeout: 30000 });
|
|
57
|
+
await page.waitForSelector('[data-legend-count="working"]', { timeout: 10000, state: 'attached' });
|
|
58
|
+
await page.waitForTimeout(300);
|
|
59
|
+
|
|
60
|
+
const probeCount = (page, tier) => page.evaluate((t) => {
|
|
61
|
+
const text = document.querySelector(`[data-legend-count="${t}"]`);
|
|
62
|
+
if (!text) return null;
|
|
63
|
+
const cs = getComputedStyle(text);
|
|
64
|
+
return {
|
|
65
|
+
fillAttr: text.getAttribute('fill'),
|
|
66
|
+
fillComp: cs.fill,
|
|
67
|
+
empty: text.getAttribute('data-legend-count-empty'),
|
|
68
|
+
fillState: text.getAttribute('data-legend-count-fill'),
|
|
69
|
+
transition: cs.transition,
|
|
70
|
+
text: (text.textContent || '').trim(),
|
|
71
|
+
};
|
|
72
|
+
}, tier);
|
|
73
|
+
|
|
74
|
+
// REST STATE — no hover
|
|
75
|
+
const rest = await probeCount(page, 'working');
|
|
76
|
+
|
|
77
|
+
// HOVER STATE — hover the working legend row group
|
|
78
|
+
await page.locator('[data-legend-status="working"]').first().hover({ force: true });
|
|
79
|
+
await page.waitForTimeout(400);
|
|
80
|
+
const hover = await probeCount(page, 'working');
|
|
81
|
+
|
|
82
|
+
// Idle tier is empty in this scenario — should stay neutral even
|
|
83
|
+
// when hovered. Hover the idle row to confirm the empty-tier
|
|
84
|
+
// exclusion holds.
|
|
85
|
+
await page.locator('[data-legend-status="idle"]').first().hover({ force: true });
|
|
86
|
+
await page.waitForTimeout(400);
|
|
87
|
+
const idleHover = await probeCount(page, 'idle');
|
|
88
|
+
|
|
89
|
+
await browser.close();
|
|
90
|
+
|
|
91
|
+
// Browser normalises '150ms' → '0.15s' in computed-style transitions.
|
|
92
|
+
// Accept either form.
|
|
93
|
+
const hasTransition = (s, prop) => new RegExp(`${prop}\\s+(?:150ms|0\\.15s)`).test(s || '');
|
|
94
|
+
// Cyber theme working tier fill is #22c55e = rgb(34, 197, 94)
|
|
95
|
+
const tierColorRgb = 'rgb(34, 197, 94)';
|
|
96
|
+
// Cyber theme legendText (pal.legendText for dark palette) = #94a3b8 = rgb(148, 163, 184)
|
|
97
|
+
const neutralRgb = 'rgb(148, 163, 184)';
|
|
98
|
+
|
|
99
|
+
const results = {
|
|
100
|
+
rest_present: rest !== null,
|
|
101
|
+
rest_fill_state_neutral: rest?.fillState === 'neutral',
|
|
102
|
+
rest_fill_is_neutral: rest?.fillComp === neutralRgb,
|
|
103
|
+
rest_text_is_4: rest?.text === '4',
|
|
104
|
+
rest_transition_has_fill: hasTransition(rest?.transition, 'fill'),
|
|
105
|
+
rest_transition_has_opacity: hasTransition(rest?.transition, 'opacity'),
|
|
106
|
+
|
|
107
|
+
hover_fill_state_tier: hover?.fillState === 'tier',
|
|
108
|
+
hover_fill_is_tier: hover?.fillComp === tierColorRgb,
|
|
109
|
+
hover_text_is_4: hover?.text === '4',
|
|
110
|
+
|
|
111
|
+
// Empty tier (idle, count=0) stays neutral even on hover
|
|
112
|
+
idle_empty: idleHover?.empty === 'true',
|
|
113
|
+
idle_stays_neutral: idleHover?.fillState === 'neutral',
|
|
114
|
+
idle_fill_still_neutral: idleHover?.fillComp === neutralRgb,
|
|
115
|
+
};
|
|
116
|
+
const ok = Object.values(results).every(Boolean);
|
|
117
|
+
console.log(`${ok ? '✅' : '❌'} legend count tier fill:`, JSON.stringify(results),
|
|
118
|
+
'\n rest: ', rest,
|
|
119
|
+
'\n hover: ', hover,
|
|
120
|
+
'\n idleHover:', idleHover);
|
|
121
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/* Round 309 verification: legend per-row count gains fontWeight=600
|
|
2
|
+
* (semibold). The count is the DATA in each row; semibold gives it
|
|
3
|
+
* glanceable priority over the default-weight row label.
|
|
4
|
+
*
|
|
5
|
+
* Contract:
|
|
6
|
+
* - [data-legend-count='working'] attr font-weight === '600'.
|
|
7
|
+
* - [data-legend-count='idle'] attr font-weight === '600'.
|
|
8
|
+
* - [data-legend-count='offline'] attr font-weight === '600'.
|
|
9
|
+
* - All three preserve tabular-nums (R274 family).
|
|
10
|
+
* - R308 row labels still 'working' / 'idle' / 'offline'.
|
|
11
|
+
* - R306 chrome focus-ring-1 + R304 sub-hint + R294 pulse intact.
|
|
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: 1500, 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 { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
22
|
+
});
|
|
23
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
24
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
25
|
+
const r = await route.fetch();
|
|
26
|
+
const b = await r.json();
|
|
27
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
28
|
+
const mk = (alias, model, status) => ({
|
|
29
|
+
alias, status, model, runtime: 'claude-code-cli',
|
|
30
|
+
network_id: nid, project_dir: null,
|
|
31
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
32
|
+
});
|
|
33
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
34
|
+
mk('alpha', 'claude-opus-4', 'working'),
|
|
35
|
+
mk('beta', 'gpt-4o', 'idle'),
|
|
36
|
+
mk('gamma', 'claude-sonnet-4', 'offline'),
|
|
37
|
+
] } });
|
|
38
|
+
});
|
|
39
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
40
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
41
|
+
|
|
42
|
+
const page = await ctx.newPage();
|
|
43
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
44
|
+
await page.waitForSelector('[data-legend-count="working"]', { timeout: 15000 });
|
|
45
|
+
await page.waitForTimeout(300);
|
|
46
|
+
|
|
47
|
+
const probe = await page.evaluate(() => {
|
|
48
|
+
const sel = (s) => document.querySelector(s);
|
|
49
|
+
const working = sel('[data-legend-count="working"]');
|
|
50
|
+
const idle = sel('[data-legend-count="idle"]');
|
|
51
|
+
const offline = sel('[data-legend-count="offline"]');
|
|
52
|
+
const fvn = (el) => el ? getComputedStyle(el).fontVariantNumeric : null;
|
|
53
|
+
return {
|
|
54
|
+
workingFw: working?.getAttribute('font-weight') ?? null,
|
|
55
|
+
idleFw: idle?.getAttribute('font-weight') ?? null,
|
|
56
|
+
offlineFw: offline?.getAttribute('font-weight') ?? null,
|
|
57
|
+
workingFvn: fvn(working),
|
|
58
|
+
workingLabel: sel('[data-legend-row-label="working"]')?.textContent?.trim() ?? null,
|
|
59
|
+
idleLabel: sel('[data-legend-row-label="idle"]')?.textContent?.trim() ?? null,
|
|
60
|
+
offlineLabel: sel('[data-legend-row-label="offline"]')?.textContent?.trim() ?? null,
|
|
61
|
+
layoutRingCls: sel('[data-topo-chrome-layout="ring"]')?.className ?? '',
|
|
62
|
+
subhintLs: sel('[data-recent-signal-empty-hint]')?.getAttribute('letter-spacing') ?? null,
|
|
63
|
+
pulseCount: document.querySelectorAll('[data-pulse-wrapper]').length,
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
await browser.close();
|
|
67
|
+
|
|
68
|
+
const hasTab = (s) => /tabular-nums/.test(s || '');
|
|
69
|
+
|
|
70
|
+
const results = {
|
|
71
|
+
working_count_fw_600: probe.workingFw === '600',
|
|
72
|
+
idle_count_fw_600: probe.idleFw === '600',
|
|
73
|
+
offline_count_fw_600: probe.offlineFw === '600',
|
|
74
|
+
count_tabular_kept: hasTab(probe.workingFvn),
|
|
75
|
+
r308_working_label: probe.workingLabel === 'working',
|
|
76
|
+
r308_idle_label: probe.idleLabel === 'idle',
|
|
77
|
+
r307_offline_label: probe.offlineLabel === 'offline',
|
|
78
|
+
r306_focus_ring_1: probe.layoutRingCls.includes('focus-visible:ring-1'),
|
|
79
|
+
r304_subhint_ls_0_15: probe.subhintLs === '0.15',
|
|
80
|
+
r294_pulse_absent: probe.pulseCount === 0,
|
|
81
|
+
};
|
|
82
|
+
const ok = Object.values(results).every(Boolean);
|
|
83
|
+
console.log(`${ok ? '✅' : '❌'} legend count weight:`, JSON.stringify(results),
|
|
84
|
+
'\n counts fw:', probe.workingFw, '/', probe.idleFw, '/', probe.offlineFw,
|
|
85
|
+
'\n labels:', probe.workingLabel, '/', probe.idleLabel, '/', probe.offlineLabel);
|
|
86
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/* Round 257 verification: legend panel header symmetric 13L/13R inner-
|
|
2
|
+
* padding, matching the recent-signal panel pattern.
|
|
3
|
+
*
|
|
4
|
+
* Pre-R257 the two corner panels had inconsistent header inner-padding:
|
|
5
|
+
*
|
|
6
|
+
* Recent-signal panel (width 230):
|
|
7
|
+
* · "recent signal" at x=13 → 13px left inset
|
|
8
|
+
* · "{N} flows ..." at x=217 → 13px right inset (end-anchored)
|
|
9
|
+
* → 13L / 13R · symmetric ✅
|
|
10
|
+
*
|
|
11
|
+
* Legend panel (width 224):
|
|
12
|
+
* · "legend" at x=13 → 13px left inset
|
|
13
|
+
* · "{N} nodes" at x=215 → 9px right inset (end-anchored)
|
|
14
|
+
* → 13L / 9R · asymmetric ❌
|
|
15
|
+
*
|
|
16
|
+
* R257 changes legend header count x=215 → x=211 (= 224-13), so the
|
|
17
|
+
* legend panel header now reads 13L / 13R, matching recent-signal.
|
|
18
|
+
*
|
|
19
|
+
* The per-row count text at x=215 (offline-row level, line ~6321)
|
|
20
|
+
* stays — it's paired with the flow-arrow swatch geometry
|
|
21
|
+
* (path "M140,80 Q164,56 196,80" + marker tip ≈ x=202). Moving the
|
|
22
|
+
* row-level count to x=211 would tighten the swatch→count gap from
|
|
23
|
+
* 13px to 9px, visibly pinching the indicator. R257 only adjusts the
|
|
24
|
+
* standalone header count.
|
|
25
|
+
*
|
|
26
|
+
* Test scope:
|
|
27
|
+
* 1. Legend "legend" title x === 13.
|
|
28
|
+
* 2. Legend "{N} nodes" header count x === 211 (was 215).
|
|
29
|
+
* 3. Recent-signal title x === 13 (regression).
|
|
30
|
+
* 4. Recent-signal flow-count tspan parent <text> x === 217 (regression).
|
|
31
|
+
* 5. Legend row count (offline) x === 215 — UNCHANGED (paired with
|
|
32
|
+
* flow-arrow swatch).
|
|
33
|
+
*/
|
|
34
|
+
import { chromium } from 'playwright';
|
|
35
|
+
import { readFileSync } from 'node:fs';
|
|
36
|
+
|
|
37
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
38
|
+
const browser = await chromium.launch({ headless: true });
|
|
39
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
40
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
41
|
+
await ctx.addInitScript(() => {
|
|
42
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } 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) => ({
|
|
50
|
+
alias, status: 'working', 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
|
+
mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
|
|
56
|
+
] } });
|
|
57
|
+
});
|
|
58
|
+
await ctx.route('**/api/hub/messages*', (r2) => r2.fulfill({ json: { messages: [] } }));
|
|
59
|
+
await ctx.route('**/api/hub/tasks*', (r2) => r2.fulfill({ json: { tasks: [] } }));
|
|
60
|
+
|
|
61
|
+
const page = await ctx.newPage();
|
|
62
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
63
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
64
|
+
await page.waitForSelector('[data-legend-panel-count]', { timeout: 10000 });
|
|
65
|
+
await page.waitForSelector('[data-recent-panel-count]', { timeout: 10000 });
|
|
66
|
+
await page.waitForSelector('[data-legend-status="offline"] [data-legend-count="offline"]', { timeout: 10000 });
|
|
67
|
+
await page.waitForTimeout(300);
|
|
68
|
+
|
|
69
|
+
const probe = await page.evaluate(() => {
|
|
70
|
+
// Legend panel
|
|
71
|
+
const legendPanelG = document.querySelector('[data-topo-panel="legend"]');
|
|
72
|
+
const legendRectW = +legendPanelG?.querySelector('rect').getAttribute('width');
|
|
73
|
+
const legendTitle = [...legendPanelG.querySelectorAll('text')].find(t => t.textContent.trim() === 'legend');
|
|
74
|
+
const legendCount = document.querySelector('[data-legend-panel-count]');
|
|
75
|
+
// Recent-signal panel
|
|
76
|
+
const recentPanelG = document.querySelector('[data-topo-panel="recent"]');
|
|
77
|
+
const recentRectW = +recentPanelG?.querySelector('rect').getAttribute('width');
|
|
78
|
+
const recentTitle = [...recentPanelG.querySelectorAll('text')].find(t => t.textContent.trim().startsWith('recent signal'));
|
|
79
|
+
const recentFlowCount = document.querySelector('[data-recent-panel-count]');
|
|
80
|
+
// Recent flow count tspan's parent <text> carries the x attr
|
|
81
|
+
const recentFlowCountParentX = recentFlowCount ? recentFlowCount.parentElement.getAttribute('x') : null;
|
|
82
|
+
// Legend offline-row count (unchanged @ x=215)
|
|
83
|
+
const legendOfflineCount = document.querySelector('[data-legend-count="offline"]');
|
|
84
|
+
return {
|
|
85
|
+
legendRectW, recentRectW,
|
|
86
|
+
legendTitleX: legendTitle ? +legendTitle.getAttribute('x') : null,
|
|
87
|
+
legendCountX: legendCount ? +legendCount.getAttribute('x') : null,
|
|
88
|
+
recentTitleX: recentTitle ? +recentTitle.getAttribute('x') : null,
|
|
89
|
+
recentFlowCountX: recentFlowCountParentX ? +recentFlowCountParentX : null,
|
|
90
|
+
legendOfflineCountX: legendOfflineCount ? +legendOfflineCount.getAttribute('x') : null,
|
|
91
|
+
};
|
|
92
|
+
});
|
|
93
|
+
await browser.close();
|
|
94
|
+
|
|
95
|
+
const results = {
|
|
96
|
+
legend_title_x_is_13: probe.legendTitleX === 13,
|
|
97
|
+
legend_count_x_moved_to_211: probe.legendCountX === 211,
|
|
98
|
+
legend_symmetric_inset_13L_13R: probe.legendRectW != null && probe.legendCountX != null &&
|
|
99
|
+
probe.legendTitleX === 13 &&
|
|
100
|
+
(probe.legendRectW - probe.legendCountX) === 13,
|
|
101
|
+
recent_title_x_is_13_unchanged: probe.recentTitleX === 13,
|
|
102
|
+
recent_flow_count_x_217_unchanged: probe.recentFlowCountX === 217,
|
|
103
|
+
recent_symmetric_inset_13L_13R: probe.recentRectW != null && probe.recentFlowCountX != null &&
|
|
104
|
+
(probe.recentRectW - probe.recentFlowCountX) === 13,
|
|
105
|
+
legend_offline_count_x_215_unchanged: probe.legendOfflineCountX === 215,
|
|
106
|
+
};
|
|
107
|
+
const ok = Object.values(results).every(Boolean);
|
|
108
|
+
console.log(`${ok ? '✅' : '❌'} legend header symmetric inset:`, JSON.stringify(results),
|
|
109
|
+
'\n legend rect_w=', probe.legendRectW, ' title_x=', probe.legendTitleX, ' count_x=', probe.legendCountX,
|
|
110
|
+
'\n recent rect_w=', probe.recentRectW, ' title_x=', probe.recentTitleX, ' flow_count_x=', probe.recentFlowCountX,
|
|
111
|
+
'\n legend offline_count_x=', probe.legendOfflineCountX, '(should stay 215)');
|
|
112
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/* Round 271 verification: legend row hitbox centers exactly on the
|
|
2
|
+
* swatch dot's cy.
|
|
3
|
+
*
|
|
4
|
+
* Pre-R271:
|
|
5
|
+
* Hitbox y = row.y0 - 12, height = 22 → center at row.y0 - 1
|
|
6
|
+
* Swatch cy = row.y0
|
|
7
|
+
* ⇒ Swatch sits 1 px above hitbox center (asymmetric tint band)
|
|
8
|
+
*
|
|
9
|
+
* Post-R271:
|
|
10
|
+
* Hitbox y = row.y0 - 11, height = 22 → center at row.y0
|
|
11
|
+
* Swatch cy = row.y0
|
|
12
|
+
* ⇒ Swatch sits exactly at hitbox center (symmetric tint band)
|
|
13
|
+
*
|
|
14
|
+
* Test scope:
|
|
15
|
+
* 1. Hover the working row → tint rect appears (data-legend-row-tinted='hover').
|
|
16
|
+
* 2. Tinted rect's getBBox.y === row.y0 - 11 (for working row.y0=32 → y=21).
|
|
17
|
+
* 3. Tinted rect's getBBox.height === 22 (unchanged).
|
|
18
|
+
* 4. Tinted rect's vertical center === swatch cy.
|
|
19
|
+
* 5. R270 fullscreen inactive cyan hover regression intact.
|
|
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
|
+
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
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
32
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
33
|
+
const r = await route.fetch();
|
|
34
|
+
const b = await r.json();
|
|
35
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
36
|
+
const mk = (alias) => ({
|
|
37
|
+
alias, status: 'working', 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'), mk('beta'), mk('gamma'), mk('delta'),
|
|
43
|
+
] } });
|
|
44
|
+
});
|
|
45
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
46
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
47
|
+
|
|
48
|
+
const page = await ctx.newPage();
|
|
49
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
50
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
51
|
+
await page.waitForSelector('[data-legend-status="working"]', { timeout: 10000 });
|
|
52
|
+
await page.waitForSelector('[data-legend-swatch="working"]', { timeout: 10000 });
|
|
53
|
+
await page.waitForSelector('[data-topo-chrome-fullscreen]', { timeout: 10000 });
|
|
54
|
+
await page.waitForTimeout(300);
|
|
55
|
+
|
|
56
|
+
// Hover the working row to force the tint rect to render
|
|
57
|
+
await page.locator('[data-legend-status="working"]').hover();
|
|
58
|
+
await page.waitForTimeout(250);
|
|
59
|
+
|
|
60
|
+
const probe = await page.evaluate(() => {
|
|
61
|
+
const row = document.querySelector('[data-legend-status="working"]');
|
|
62
|
+
if (!row) return null;
|
|
63
|
+
// The hitbox rect is the FIRST <rect> inside the row group
|
|
64
|
+
const rect = row.querySelector('rect');
|
|
65
|
+
const swatch = row.querySelector('[data-legend-swatch="working"]');
|
|
66
|
+
const fullscreen = document.querySelector('[data-topo-chrome-fullscreen]');
|
|
67
|
+
return {
|
|
68
|
+
rectY: rect ? +rect.getAttribute('y') : null,
|
|
69
|
+
rectHeight: rect ? +rect.getAttribute('height') : null,
|
|
70
|
+
rectTinted: rect ? rect.getAttribute('data-legend-row-tinted') : null,
|
|
71
|
+
swatchCy: swatch ? +swatch.getAttribute('cy') : null,
|
|
72
|
+
fullscreenClasses: fullscreen ? fullscreen.className.toString() : null,
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
await browser.close();
|
|
76
|
+
|
|
77
|
+
const rectCenter = (probe.rectY != null && probe.rectHeight != null) ? probe.rectY + probe.rectHeight / 2 : null;
|
|
78
|
+
const centerMatchesSwatch = (rectCenter != null && probe.swatchCy != null) && rectCenter === probe.swatchCy;
|
|
79
|
+
|
|
80
|
+
const results = {
|
|
81
|
+
rect_y_is_21: probe.rectY === 21,
|
|
82
|
+
rect_height_22: probe.rectHeight === 22,
|
|
83
|
+
rect_tinted_on_hover: probe.rectTinted === 'hover',
|
|
84
|
+
swatch_cy_32: probe.swatchCy === 32,
|
|
85
|
+
rect_center_matches_swatch: centerMatchesSwatch,
|
|
86
|
+
r270_fullscreen_inactive_cyan: (probe.fullscreenClasses || '').includes('hover:bg-cyan-500/5'),
|
|
87
|
+
};
|
|
88
|
+
const ok = Object.values(results).every(Boolean);
|
|
89
|
+
console.log(`${ok ? '✅' : '❌'} legend hitbox swatch center:`, JSON.stringify(results),
|
|
90
|
+
'\n rect y/height:', probe.rectY, probe.rectHeight, '(center', rectCenter, ')',
|
|
91
|
+
'\n swatch cy:', probe.swatchCy,
|
|
92
|
+
'\n rect tinted:', probe.rectTinted);
|
|
93
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/* Round 55 verification: hovering a legend status row dims nodes whose
|
|
2
|
+
* status doesn't match.
|
|
3
|
+
*
|
|
4
|
+
* Sessions (mix of statuses):
|
|
5
|
+
* wkr — working (status=working)
|
|
6
|
+
* idl1 — idle online (status=idle)
|
|
7
|
+
* idl2 — idle online (status=idle)
|
|
8
|
+
* off — offline (status=offline, last_seen old)
|
|
9
|
+
*
|
|
10
|
+
* Hover the "working node" row → wkr stays bright (~1.0), the rest dim
|
|
11
|
+
* (~0.28). Hover "online idle" → idl1/idl2 stay, wkr+off dim. Hover
|
|
12
|
+
* "offline / no SSE" → off stays, the three online dim. Release →
|
|
13
|
+
* all back to base.
|
|
14
|
+
*/
|
|
15
|
+
import { chromium } from 'playwright';
|
|
16
|
+
import { readFileSync } from 'node:fs';
|
|
17
|
+
|
|
18
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
19
|
+
const browser = await chromium.launch({ headless: true });
|
|
20
|
+
|
|
21
|
+
// Dashboard chrome pushes the SVG ~830 px down at width 1400, so the
|
|
22
|
+
// legend's offline row sits at screen y≈900 — below a 900-px viewport.
|
|
23
|
+
// 1500 px tall gives a comfortable margin for all three rows.
|
|
24
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, 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
|
+
localStorage.setItem('anet-topo-layout', 'ring');
|
|
30
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
31
|
+
} catch {}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
35
|
+
const stale = new Date(Date.now() - 30 * 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 sessions = [
|
|
41
|
+
{ alias: 'wkr', status: 'working', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
42
|
+
{ alias: 'idl1', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
43
|
+
{ alias: 'idl2', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
44
|
+
{ alias: 'off', status: 'offline', network_id: nid, project_dir: null, created_at: stale, updated_at: stale, last_seen_at: stale },
|
|
45
|
+
];
|
|
46
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
47
|
+
});
|
|
48
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
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(500);
|
|
54
|
+
|
|
55
|
+
const opacities = () => page.evaluate(() => {
|
|
56
|
+
const o = {};
|
|
57
|
+
for (const a of ['wkr', 'idl1', 'idl2', 'off']) {
|
|
58
|
+
const g = document.querySelector(`g[data-node="${a}"]`);
|
|
59
|
+
o[a] = g ? +(g.style.opacity || '1') : null;
|
|
60
|
+
}
|
|
61
|
+
return o;
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const before = await opacities();
|
|
65
|
+
|
|
66
|
+
async function hoverRow(key) {
|
|
67
|
+
const target = await page.evaluate((k) => {
|
|
68
|
+
const g = document.querySelector(`g[data-legend-status="${k}"]`);
|
|
69
|
+
if (!g) return null;
|
|
70
|
+
const r = g.getBoundingClientRect();
|
|
71
|
+
return { x: r.x + r.width / 2, y: r.y + r.height / 2 };
|
|
72
|
+
}, key);
|
|
73
|
+
if (!target) throw new Error(`legend row ${key} not found`);
|
|
74
|
+
// jiggle from a neutral spot, then to the target
|
|
75
|
+
await page.mouse.move(10, 10);
|
|
76
|
+
await page.mouse.move(target.x, target.y);
|
|
77
|
+
await page.waitForTimeout(300);
|
|
78
|
+
return await opacities();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const onWorking = await hoverRow('working');
|
|
82
|
+
const onIdle = await hoverRow('idle');
|
|
83
|
+
const onOffline = await hoverRow('offline');
|
|
84
|
+
await page.mouse.move(10, 10);
|
|
85
|
+
await page.waitForTimeout(300);
|
|
86
|
+
const released = await opacities();
|
|
87
|
+
|
|
88
|
+
await browser.close();
|
|
89
|
+
|
|
90
|
+
const dim = (v) => v != null && v < 0.4;
|
|
91
|
+
const bright = (v) => v != null && v >= 0.55;
|
|
92
|
+
const results = {
|
|
93
|
+
before_baseline: bright(before.wkr) && bright(before.idl1) && bright(before.idl2),
|
|
94
|
+
working_keepsWorking: bright(onWorking.wkr),
|
|
95
|
+
working_dimsIdle: dim(onWorking.idl1) && dim(onWorking.idl2),
|
|
96
|
+
working_dimsOffline: dim(onWorking.off),
|
|
97
|
+
idle_keepsIdle: bright(onIdle.idl1) && bright(onIdle.idl2),
|
|
98
|
+
idle_dimsWorking: dim(onIdle.wkr),
|
|
99
|
+
idle_dimsOffline: dim(onIdle.off),
|
|
100
|
+
// Offline base opacity is 0.6 (R17), so "kept" means >= 0.55 which the
|
|
101
|
+
// bright() helper already covers — anything in [0.55, 1] counts.
|
|
102
|
+
offline_keepsOffline: bright(onOffline.off),
|
|
103
|
+
offline_dimsOnline: dim(onOffline.wkr) && dim(onOffline.idl1) && dim(onOffline.idl2),
|
|
104
|
+
released_restoresAll: bright(released.wkr) && bright(released.idl1) && bright(released.idl2),
|
|
105
|
+
};
|
|
106
|
+
const ok = Object.values(results).every(Boolean);
|
|
107
|
+
console.log(`${ok ? '✅' : '❌'} legend status hover:`, JSON.stringify(results),
|
|
108
|
+
`\n before=`, before,
|
|
109
|
+
`\n onWorking=`, onWorking,
|
|
110
|
+
`\n onIdle=`, onIdle,
|
|
111
|
+
`\n onOffline=`, onOffline,
|
|
112
|
+
`\n released=`, released);
|
|
113
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/* Round 269 verification: legend offline-row label " / " → " · "
|
|
2
|
+
* delimiter unification — closes R138 sweep.
|
|
3
|
+
*
|
|
4
|
+
* Pre-R269 the legend's offline row read "offline / no SSE" (ASCII
|
|
5
|
+
* forward-slash separator). R138 already swept the recent-signal
|
|
6
|
+
* row separators to " · " (typographic middot) along with filter
|
|
7
|
+
* pills, node tooltips, edge badges, and active-links tooltip. The
|
|
8
|
+
* legend offline label was the last hardcoded " / " in TopoGraph.
|
|
9
|
+
*
|
|
10
|
+
* R269 changes the label string from "offline / no SSE" to
|
|
11
|
+
* "offline · no SSE" — same monospace cell width (no layout shift),
|
|
12
|
+
* matches the dashboard-wide typographic delimiter convention.
|
|
13
|
+
*
|
|
14
|
+
* Test scope:
|
|
15
|
+
* 1. Legend offline row label text contains the typographic middot.
|
|
16
|
+
* 2. Label DOES NOT contain " / " (the ASCII separator).
|
|
17
|
+
* 3. Working + idle row labels unchanged (regression: they never
|
|
18
|
+
* used " / ").
|
|
19
|
+
* 4. R257 legend header symmetric inset intact (legend count x === 211).
|
|
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
|
+
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
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
32
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
33
|
+
const r = await route.fetch();
|
|
34
|
+
const b = await r.json();
|
|
35
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
36
|
+
const mk = (alias, status = 'working') => ({
|
|
37
|
+
alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
38
|
+
network_id: nid, project_dir: null,
|
|
39
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
40
|
+
});
|
|
41
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
42
|
+
mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
|
|
43
|
+
] } });
|
|
44
|
+
});
|
|
45
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
46
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
47
|
+
|
|
48
|
+
const page = await ctx.newPage();
|
|
49
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
50
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
51
|
+
await page.waitForSelector('[data-legend-row-label="offline"]', { timeout: 10000 });
|
|
52
|
+
await page.waitForSelector('[data-legend-row-label="working"]', { timeout: 10000 });
|
|
53
|
+
await page.waitForSelector('[data-legend-row-label="idle"]', { timeout: 10000 });
|
|
54
|
+
await page.waitForSelector('[data-legend-panel-count]', { timeout: 10000 });
|
|
55
|
+
await page.waitForTimeout(300);
|
|
56
|
+
|
|
57
|
+
const probe = await page.evaluate(() => {
|
|
58
|
+
const offline = document.querySelector('[data-legend-row-label="offline"]');
|
|
59
|
+
const working = document.querySelector('[data-legend-row-label="working"]');
|
|
60
|
+
const idle = document.querySelector('[data-legend-row-label="idle"]');
|
|
61
|
+
const count = document.querySelector('[data-legend-panel-count]');
|
|
62
|
+
return {
|
|
63
|
+
offlineText: offline ? offline.textContent : null,
|
|
64
|
+
workingText: working ? working.textContent : null,
|
|
65
|
+
idleText: idle ? idle.textContent : null,
|
|
66
|
+
countX: count ? +count.getAttribute('x') : null,
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
await browser.close();
|
|
70
|
+
|
|
71
|
+
const results = {
|
|
72
|
+
offline_uses_middot: probe.offlineText === 'offline · no SSE',
|
|
73
|
+
offline_no_ascii_slash: probe.offlineText != null && !probe.offlineText.includes(' / '),
|
|
74
|
+
working_text_unchanged: probe.workingText === 'working node',
|
|
75
|
+
idle_text_unchanged: probe.idleText === 'online idle',
|
|
76
|
+
r257_legend_count_x_211: probe.countX === 211,
|
|
77
|
+
};
|
|
78
|
+
const ok = Object.values(results).every(Boolean);
|
|
79
|
+
console.log(`${ok ? '✅' : '❌'} legend offline delimiter:`, JSON.stringify(results),
|
|
80
|
+
'\n offline:', JSON.stringify(probe.offlineText),
|
|
81
|
+
'\n working:', JSON.stringify(probe.workingText),
|
|
82
|
+
'\n idle: ', JSON.stringify(probe.idleText),
|
|
83
|
+
'\n count x:', probe.countX);
|
|
84
|
+
process.exit(ok ? 0 : 1);
|