@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,80 @@
|
|
|
1
|
+
/* Round 308 verification: continue R307 legend label 减法.
|
|
2
|
+
* 'working node' → 'working' (drops 'node' qualifier).
|
|
3
|
+
* 'online idle' → 'idle' (drops 'online' qualifier).
|
|
4
|
+
* Plus R307 'offline' (already simplified).
|
|
5
|
+
*
|
|
6
|
+
* After R307+R308 all three legend labels are just status words:
|
|
7
|
+
* working / idle / offline — 3-state list at 7 / 4 / 7 chars.
|
|
8
|
+
*
|
|
9
|
+
* Contract:
|
|
10
|
+
* - [data-legend-row-label='working'] text === 'working'.
|
|
11
|
+
* - [data-legend-row-label='idle'] text === 'idle'.
|
|
12
|
+
* - [data-legend-row-label='offline'] text === 'offline' (R307).
|
|
13
|
+
* - No labels contain ' node' or 'online ' substrings.
|
|
14
|
+
* - R306 focus-ring-1 + R304 sub-hint ls=0.15 + R294 pulse absent intact.
|
|
15
|
+
*/
|
|
16
|
+
import { chromium } from 'playwright';
|
|
17
|
+
import { readFileSync } from 'node:fs';
|
|
18
|
+
|
|
19
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
20
|
+
const browser = await chromium.launch({ headless: true });
|
|
21
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
22
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
23
|
+
await ctx.addInitScript(() => {
|
|
24
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
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, model, status) => ({
|
|
32
|
+
alias, status, model, runtime: 'claude-code-cli',
|
|
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('alpha', 'claude-opus-4', 'working'),
|
|
38
|
+
mk('beta', 'gpt-4o', 'idle'),
|
|
39
|
+
mk('gamma', 'claude-sonnet-4', 'offline'),
|
|
40
|
+
] } });
|
|
41
|
+
});
|
|
42
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
43
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
44
|
+
|
|
45
|
+
const page = await ctx.newPage();
|
|
46
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
47
|
+
await page.waitForSelector('[data-legend-row-label="offline"]', { timeout: 15000 });
|
|
48
|
+
await page.waitForTimeout(300);
|
|
49
|
+
|
|
50
|
+
const probe = await page.evaluate(() => {
|
|
51
|
+
const sel = (s) => document.querySelector(s);
|
|
52
|
+
const txt = (el) => (el?.textContent || '').trim();
|
|
53
|
+
const layoutRing = sel('[data-topo-chrome-layout="ring"]');
|
|
54
|
+
return {
|
|
55
|
+
workingText: txt(sel('[data-legend-row-label="working"]')),
|
|
56
|
+
idleText: txt(sel('[data-legend-row-label="idle"]')),
|
|
57
|
+
offlineText: txt(sel('[data-legend-row-label="offline"]')),
|
|
58
|
+
layoutRingCls: layoutRing?.className ?? '',
|
|
59
|
+
subhintLs: sel('[data-recent-signal-empty-hint]')?.getAttribute('letter-spacing') ?? null,
|
|
60
|
+
pulseCount: document.querySelectorAll('[data-pulse-wrapper]').length,
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
await browser.close();
|
|
64
|
+
|
|
65
|
+
const results = {
|
|
66
|
+
working_simplified: probe.workingText === 'working',
|
|
67
|
+
idle_simplified: probe.idleText === 'idle',
|
|
68
|
+
offline_kept_r307: probe.offlineText === 'offline',
|
|
69
|
+
no_node_qualifier: !/ node\b/i.test(probe.workingText),
|
|
70
|
+
no_online_qualifier: !/online /i.test(probe.idleText),
|
|
71
|
+
r306_focus_ring_1_kept: probe.layoutRingCls.includes('focus-visible:ring-1'),
|
|
72
|
+
r304_subhint_ls_0_15: probe.subhintLs === '0.15',
|
|
73
|
+
r294_pulse_absent: probe.pulseCount === 0,
|
|
74
|
+
};
|
|
75
|
+
const ok = Object.values(results).every(Boolean);
|
|
76
|
+
console.log(`${ok ? '✅' : '❌'} legend rows simplified:`, JSON.stringify(results),
|
|
77
|
+
'\n working:', JSON.stringify(probe.workingText),
|
|
78
|
+
'\n idle:', JSON.stringify(probe.idleText),
|
|
79
|
+
'\n offline:', JSON.stringify(probe.offlineText));
|
|
80
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/* Round 219 verification: legend row text gets letter-spacing pin
|
|
2
|
+
* signature 0px → 0.5px, mirroring R218 group label treatment.
|
|
3
|
+
* Pre-R219 hover and pin shared fill colour and were typographically
|
|
4
|
+
* identical at letter form.
|
|
5
|
+
*
|
|
6
|
+
* Test (cyber dark, 4 working sessions):
|
|
7
|
+
* 1. Three legend rows (working / idle / offline) render with
|
|
8
|
+
* data-legend-row-label.
|
|
9
|
+
* 2. Idle: data-legend-row-label-pinned='false' on all rows,
|
|
10
|
+
* computed letter-spacing ~ 0 (CSS resolves '0px' → 'normal').
|
|
11
|
+
* 3. Inline transition includes 'letter-spacing 150ms'.
|
|
12
|
+
* 4. Click the working legend row → that row becomes pinned.
|
|
13
|
+
* 5. Working row: pinned='true', letter-spacing ~ 0.5px.
|
|
14
|
+
* 6. Other rows (idle / offline) stay unpinned + 0 spacing.
|
|
15
|
+
* 7. Click pinned row again → unpin → back to 0 spacing.
|
|
16
|
+
*
|
|
17
|
+
* Same 'normal' vs '0px' CSS-equivalence parser as R218 (CSS
|
|
18
|
+
* resolves letter-spacing: 0px to keyword 'normal' in computed style).
|
|
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
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
43
|
+
mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
|
|
44
|
+
] } });
|
|
45
|
+
});
|
|
46
|
+
await ctx.route('**/api/hub/messages*', (r2) => r2.fulfill({ json: { messages: [] } }));
|
|
47
|
+
await ctx.route('**/api/hub/tasks*', (r2) => r2.fulfill({ json: { tasks: [] } }));
|
|
48
|
+
|
|
49
|
+
const page = await ctx.newPage();
|
|
50
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
51
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
52
|
+
await page.waitForSelector('[data-legend-row-label]', { timeout: 10000, state: 'attached' });
|
|
53
|
+
await page.waitForTimeout(300);
|
|
54
|
+
|
|
55
|
+
const probe = async () => page.evaluate(() => {
|
|
56
|
+
return [...document.querySelectorAll('[data-legend-row-label]')].map(el => {
|
|
57
|
+
const cs = getComputedStyle(el);
|
|
58
|
+
return {
|
|
59
|
+
key: el.getAttribute('data-legend-row-label'),
|
|
60
|
+
pinned: el.getAttribute('data-legend-row-label-pinned'),
|
|
61
|
+
spacing: cs.letterSpacing,
|
|
62
|
+
transition: el.style.transition,
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const idle = await probe();
|
|
68
|
+
|
|
69
|
+
// Click the working row to pin it. Use the row wrapper <g
|
|
70
|
+
// data-legend-status="working"> to ensure the click hits the hit
|
|
71
|
+
// area (the text itself has pointerEvents:'none' via the parent <g>).
|
|
72
|
+
await page.click('[data-legend-status="working"]');
|
|
73
|
+
await page.waitForTimeout(200);
|
|
74
|
+
const pinned = await probe();
|
|
75
|
+
|
|
76
|
+
await page.click('[data-legend-status="working"]');
|
|
77
|
+
await page.waitForTimeout(200);
|
|
78
|
+
const unpinned = await probe();
|
|
79
|
+
|
|
80
|
+
await browser.close();
|
|
81
|
+
|
|
82
|
+
// CSS resolves letter-spacing: 0px to 'normal' in computed style.
|
|
83
|
+
// Accept both forms as "zero spacing".
|
|
84
|
+
const near = (s, want, tol = 0.15) => {
|
|
85
|
+
if (want === 0 && (s === 'normal' || s === '0px' || s === '0' || !s)) return true;
|
|
86
|
+
const got = parseFloat(s || '0');
|
|
87
|
+
return Math.abs(got - want) < tol;
|
|
88
|
+
};
|
|
89
|
+
const hasLetterSpacingT = (s) => /letter-spacing\s+(?:150ms|0\.15s)/.test(s || '');
|
|
90
|
+
|
|
91
|
+
const idleWorking = idle.find(p => p.key === 'working');
|
|
92
|
+
const idleIdle = idle.find(p => p.key === 'idle');
|
|
93
|
+
const idleOffline = idle.find(p => p.key === 'offline');
|
|
94
|
+
const pinWorking = pinned.find(p => p.key === 'working');
|
|
95
|
+
const pinIdle = pinned.find(p => p.key === 'idle');
|
|
96
|
+
const unpinWorking = unpinned.find(p => p.key === 'working');
|
|
97
|
+
|
|
98
|
+
const results = {
|
|
99
|
+
three_rows_present: idle.length === 3,
|
|
100
|
+
has_working: !!idleWorking,
|
|
101
|
+
has_idle: !!idleIdle,
|
|
102
|
+
has_offline: !!idleOffline,
|
|
103
|
+
|
|
104
|
+
// Idle: all unpinned, spacing 0, transition present
|
|
105
|
+
all_idle_unpinned: idle.every(p => p.pinned === 'false'),
|
|
106
|
+
all_idle_spacing_zero: idle.every(p => near(p.spacing, 0)),
|
|
107
|
+
all_have_transition: idle.every(p => hasLetterSpacingT(p.transition)),
|
|
108
|
+
|
|
109
|
+
// After pin click: working pinned, idle unchanged
|
|
110
|
+
working_pinned_attr: pinWorking?.pinned === 'true',
|
|
111
|
+
working_spacing_half: near(pinWorking?.spacing, 0.5),
|
|
112
|
+
idle_stays_unpinned: pinIdle?.pinned === 'false',
|
|
113
|
+
idle_spacing_unchanged: near(pinIdle?.spacing, 0),
|
|
114
|
+
|
|
115
|
+
// After unpin click: working back to unpinned + 0 spacing
|
|
116
|
+
unpin_attr_false: unpinWorking?.pinned === 'false',
|
|
117
|
+
unpin_spacing_zero: near(unpinWorking?.spacing, 0),
|
|
118
|
+
};
|
|
119
|
+
const ok = Object.values(results).every(Boolean);
|
|
120
|
+
console.log(`${ok ? '✅' : '❌'} legend-row pin spacing:`, JSON.stringify(results),
|
|
121
|
+
`\n idle:`, idle,
|
|
122
|
+
`\n pinned:`, pinned,
|
|
123
|
+
`\n unpinned:`, unpinned);
|
|
124
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/* Round 105 verification: legend rows in the SVG panel pick up a
|
|
2
|
+
* row-background tint on hover and pin, mirroring R104's treatment
|
|
3
|
+
* on the recent-signal panel. The tint uses the ROW'S OWN swatch
|
|
4
|
+
* colour (working=green, idle=teal, offline=slate) so the user's
|
|
5
|
+
* eye associates the highlight with its swatch instead of a generic
|
|
6
|
+
* accent.
|
|
7
|
+
*
|
|
8
|
+
* Verifies idle / hover / pinned states via the new data-legend-
|
|
9
|
+
* row-tinted attribute + actual fill/opacity. Pin is stronger than
|
|
10
|
+
* hover; idle is transparent.
|
|
11
|
+
*/
|
|
12
|
+
import { chromium } from 'playwright';
|
|
13
|
+
import { readFileSync } from 'node:fs';
|
|
14
|
+
|
|
15
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
16
|
+
const browser = await chromium.launch({ headless: true });
|
|
17
|
+
|
|
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
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
24
|
+
sessionStorage.removeItem('anet-topo-pinned-status');
|
|
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, status) => ({
|
|
34
|
+
alias, status, 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('wkr1', 'working'),
|
|
40
|
+
mk('idl1', 'idle'),
|
|
41
|
+
mk('off1', 'offline'),
|
|
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('g[data-node]').length === 3, { timeout: 30000 });
|
|
49
|
+
await page.waitForTimeout(400);
|
|
50
|
+
|
|
51
|
+
const readRow = (key) => page.evaluate((k) => {
|
|
52
|
+
const g = document.querySelector(`[data-legend-status="${k}"]`);
|
|
53
|
+
if (!g) return null;
|
|
54
|
+
const rect = g.querySelector('rect');
|
|
55
|
+
return {
|
|
56
|
+
tinted: rect?.getAttribute('data-legend-row-tinted') || '',
|
|
57
|
+
fill: rect?.getAttribute('fill') || '',
|
|
58
|
+
opacity: rect?.getAttribute('opacity') || '',
|
|
59
|
+
};
|
|
60
|
+
}, key);
|
|
61
|
+
|
|
62
|
+
const before = await Promise.all(['working', 'idle', 'offline'].map(readRow));
|
|
63
|
+
|
|
64
|
+
// Hover working.
|
|
65
|
+
await page.locator('[data-legend-status="working"]').hover();
|
|
66
|
+
await page.waitForTimeout(250);
|
|
67
|
+
const onHoverWorking = await Promise.all(['working', 'idle', 'offline'].map(readRow));
|
|
68
|
+
|
|
69
|
+
// Move away — clears hover.
|
|
70
|
+
await page.mouse.move(10, 10);
|
|
71
|
+
await page.waitForTimeout(250);
|
|
72
|
+
|
|
73
|
+
// Pin idle.
|
|
74
|
+
await page.locator('[data-legend-status="idle"]').click();
|
|
75
|
+
await page.waitForTimeout(250);
|
|
76
|
+
const onPinIdle = await Promise.all(['working', 'idle', 'offline'].map(readRow));
|
|
77
|
+
|
|
78
|
+
await browser.close();
|
|
79
|
+
|
|
80
|
+
const opacity = (s) => parseFloat(s) || 0;
|
|
81
|
+
const results = {
|
|
82
|
+
before_allTransparent: before.every(r => r?.tinted === 'none' && r?.fill === 'transparent'),
|
|
83
|
+
hoverWorking_workingTinted: onHoverWorking[0]?.tinted === 'hover' && onHoverWorking[0]?.fill !== 'transparent',
|
|
84
|
+
hoverWorking_othersUnchanged: onHoverWorking.slice(1).every(r => r?.tinted === 'none'),
|
|
85
|
+
hoverWorking_opacityLow: opacity(onHoverWorking[0]?.opacity || '') > 0 && opacity(onHoverWorking[0]?.opacity || '') < 0.2,
|
|
86
|
+
pinIdle_idlePinned: onPinIdle[1]?.tinted === 'pinned' && onPinIdle[1]?.fill !== 'transparent',
|
|
87
|
+
pinIdle_pinStrongerThanHover: opacity(onPinIdle[1]?.opacity || '') > opacity(onHoverWorking[0]?.opacity || ''),
|
|
88
|
+
pinIdle_otherRowsClean: onPinIdle.filter((_, i) => i !== 1).every(r => r?.tinted === 'none'),
|
|
89
|
+
};
|
|
90
|
+
const ok = Object.values(results).every(Boolean);
|
|
91
|
+
console.log(`${ok ? '✅' : '❌'} legend-row tint:`, JSON.stringify(results),
|
|
92
|
+
`\n before=`, before,
|
|
93
|
+
`\n onHoverWorking=`, onHoverWorking,
|
|
94
|
+
`\n onPinIdle=`, onPinIdle);
|
|
95
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/* Round 149 verification: legend rows gain a <title> tooltip
|
|
2
|
+
* symmetric with R148's recent-signal row tooltip. Closes the
|
|
3
|
+
* R97 idiom on the panel side — anywhere the UI shows "N" should
|
|
4
|
+
* hover-explain WHICH N.
|
|
5
|
+
*
|
|
6
|
+
* Both side panels now have matching row-tooltip affordances:
|
|
7
|
+
* recent-signal row (R148): full from/to/timestamp/content + pin hint
|
|
8
|
+
* legend row (R149): status label + matched-aliases + pin hint
|
|
9
|
+
*
|
|
10
|
+
* Fleet: 3 working / 2 idle / 1 offline so all three bucket rows
|
|
11
|
+
* show non-zero. Verify each row's title contains:
|
|
12
|
+
* - status label + count
|
|
13
|
+
* - alias list of matching nodes
|
|
14
|
+
* - "click to pin" hint when not pinned
|
|
15
|
+
*
|
|
16
|
+
* Then click idle to pin → title flips to "click to release pin".
|
|
17
|
+
*/
|
|
18
|
+
import { chromium } from 'playwright';
|
|
19
|
+
import { readFileSync } from 'node:fs';
|
|
20
|
+
|
|
21
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
22
|
+
const browser = await chromium.launch({ headless: true });
|
|
23
|
+
|
|
24
|
+
const ctx = await browser.newContext({ viewport: { width: 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 { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } 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, status) => ({
|
|
36
|
+
alias, status, 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
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
41
|
+
mk('worker-a', 'working'), mk('worker-b', 'working'), mk('worker-c', 'working'),
|
|
42
|
+
mk('idle-a', 'idle'), mk('idle-b', 'idle'),
|
|
43
|
+
mk('off-a', 'offline'),
|
|
44
|
+
] } });
|
|
45
|
+
});
|
|
46
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
47
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
48
|
+
|
|
49
|
+
const page = await ctx.newPage();
|
|
50
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
51
|
+
// 3 working + 2 idle + 1 offline = 6 sessions; offline nodes still
|
|
52
|
+
// render as g[data-node] (line ~2754 [...onlineNodes, ...offlineNodes]
|
|
53
|
+
// .map). Initial count check was off-by-one.
|
|
54
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 6, { timeout: 30000 });
|
|
55
|
+
await page.waitForSelector('[data-legend-status="working"]', { timeout: 10000 });
|
|
56
|
+
await page.waitForTimeout(400);
|
|
57
|
+
|
|
58
|
+
const readTitle = (key) => page.evaluate((k) => {
|
|
59
|
+
const row = document.querySelector(`[data-legend-status="${k}"]`);
|
|
60
|
+
return row?.querySelector('title')?.textContent || '';
|
|
61
|
+
}, key);
|
|
62
|
+
|
|
63
|
+
const wTitle = await readTitle('working');
|
|
64
|
+
const iTitle = await readTitle('idle');
|
|
65
|
+
const oTitle = await readTitle('offline');
|
|
66
|
+
|
|
67
|
+
// Pin idle → its title flips
|
|
68
|
+
await page.locator('[data-legend-status="idle"]').click();
|
|
69
|
+
await page.waitForTimeout(200);
|
|
70
|
+
const iPinnedTitle = await readTitle('idle');
|
|
71
|
+
|
|
72
|
+
await browser.close();
|
|
73
|
+
|
|
74
|
+
const results = {
|
|
75
|
+
// Working row: 3 working aliases
|
|
76
|
+
w_hasLabel: wTitle.includes('working node · 3'),
|
|
77
|
+
w_hasWorkerA: wTitle.includes('worker-a'),
|
|
78
|
+
w_hasWorkerB: wTitle.includes('worker-b'),
|
|
79
|
+
w_hasWorkerC: wTitle.includes('worker-c'),
|
|
80
|
+
w_pinHint: wTitle.includes('click to pin'),
|
|
81
|
+
|
|
82
|
+
// Idle row: 2 idle aliases
|
|
83
|
+
i_hasLabel: iTitle.includes('online idle · 2'),
|
|
84
|
+
i_hasIdleA: iTitle.includes('idle-a'),
|
|
85
|
+
i_hasIdleB: iTitle.includes('idle-b'),
|
|
86
|
+
i_pinHint: iTitle.includes('click to pin'),
|
|
87
|
+
|
|
88
|
+
// Offline row: 1 offline alias
|
|
89
|
+
o_hasLabel: oTitle.includes('offline / no SSE · 1'),
|
|
90
|
+
o_hasOffA: oTitle.includes('off-a'),
|
|
91
|
+
|
|
92
|
+
// After pin: title hint flipped
|
|
93
|
+
iPinned_releaseHint: iPinnedTitle.includes('click to release pin'),
|
|
94
|
+
iPinned_noPinHint: !iPinnedTitle.includes('click to pin · hover to preview'),
|
|
95
|
+
};
|
|
96
|
+
const ok = Object.values(results).every(Boolean);
|
|
97
|
+
console.log(`${ok ? '✅' : '❌'} legend-row title:`, JSON.stringify(results),
|
|
98
|
+
`\n workingTitle:\n${wTitle}`,
|
|
99
|
+
`\n idleTitle:\n${iTitle}`,
|
|
100
|
+
`\n offlineTitle:\n${oTitle}`,
|
|
101
|
+
`\n idlePinnedTitle:\n${iPinnedTitle}`);
|
|
102
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/* Round 197 verification: legend swatch dot scales r 5.5 → 7 when
|
|
2
|
+
* its row is hovered or pinned. Mirrors R177 hub hover-lift but at
|
|
3
|
+
* legend grain.
|
|
4
|
+
*
|
|
5
|
+
* Pre-R197 the swatch was a flat constant r=5.5 — every other piece
|
|
6
|
+
* of the row responded to hover/pin (R55 text brighten, R105 bg
|
|
7
|
+
* tint, R144 1px row lift, R181 pin ring) but the swatch dot
|
|
8
|
+
* itself stayed still. The dot is the row's visual-identity
|
|
9
|
+
* anchor; making it grow gives the gesture full row-wide reach.
|
|
10
|
+
*
|
|
11
|
+
* Test (cyber/dark theme, 4 working sessions):
|
|
12
|
+
* 1. Three swatches present: working / idle / offline.
|
|
13
|
+
* 2. Idle: data-legend-swatch-state='idle', r ~ 5.5.
|
|
14
|
+
* 3. Hover the "working" legend row, wait past 150ms transition.
|
|
15
|
+
* 4. Working swatch: state='hover', r ~ 7.
|
|
16
|
+
* 5. Other swatches (idle/offline) stay 'idle' + r ~ 5.5.
|
|
17
|
+
* 6. Move away, wait — working swatch back to 'idle' + r 5.5.
|
|
18
|
+
* 7. transition: r 150ms ease-out is in the inline style.
|
|
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, status = 'working') => ({
|
|
38
|
+
alias, status, model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
39
|
+
network_id: nid, project_dir: null,
|
|
40
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
41
|
+
});
|
|
42
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
43
|
+
mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
|
|
44
|
+
] } });
|
|
45
|
+
});
|
|
46
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
47
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
48
|
+
|
|
49
|
+
const page = await ctx.newPage();
|
|
50
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
51
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
52
|
+
await page.waitForSelector('[data-legend-swatch]', { timeout: 10000 });
|
|
53
|
+
await page.waitForTimeout(300);
|
|
54
|
+
|
|
55
|
+
const probe = async () => page.evaluate(() => {
|
|
56
|
+
return [...document.querySelectorAll('[data-legend-swatch]')].map(el => ({
|
|
57
|
+
key: el.getAttribute('data-legend-swatch'),
|
|
58
|
+
state: el.getAttribute('data-legend-swatch-state'),
|
|
59
|
+
// Read both computed CSS r (the transitionable property) and the
|
|
60
|
+
// SVG attribute fallback. Chrome computes the CSS r as the live
|
|
61
|
+
// value, including mid-transition interpolated values.
|
|
62
|
+
cssR: parseFloat(getComputedStyle(el).r),
|
|
63
|
+
attrR: parseFloat(el.getAttribute('r') || ''),
|
|
64
|
+
transition: el.style.transition,
|
|
65
|
+
}));
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const idle = await probe();
|
|
69
|
+
|
|
70
|
+
await page.hover('[data-legend-status="working"]');
|
|
71
|
+
await page.waitForTimeout(220); // past 150ms r transition + margin
|
|
72
|
+
const hovered = await probe();
|
|
73
|
+
|
|
74
|
+
await page.mouse.move(0, 0);
|
|
75
|
+
await page.waitForTimeout(220);
|
|
76
|
+
const settled = await probe();
|
|
77
|
+
|
|
78
|
+
await browser.close();
|
|
79
|
+
|
|
80
|
+
const idleWorking = idle.find(s => s.key === 'working');
|
|
81
|
+
const idleIdle = idle.find(s => s.key === 'idle');
|
|
82
|
+
const idleOffline = idle.find(s => s.key === 'offline');
|
|
83
|
+
|
|
84
|
+
const hovWorking = hovered.find(s => s.key === 'working');
|
|
85
|
+
const hovIdle = hovered.find(s => s.key === 'idle');
|
|
86
|
+
const hovOffline = hovered.find(s => s.key === 'offline');
|
|
87
|
+
|
|
88
|
+
const settledWorking = settled.find(s => s.key === 'working');
|
|
89
|
+
|
|
90
|
+
const results = {
|
|
91
|
+
three_swatches: idle.length === 3,
|
|
92
|
+
has_working_swatch: !!idleWorking,
|
|
93
|
+
has_idle_swatch: !!idleIdle,
|
|
94
|
+
has_offline_swatch: !!idleOffline,
|
|
95
|
+
|
|
96
|
+
idle_working_state_idle: idleWorking?.state === 'idle',
|
|
97
|
+
idle_working_r_small: Math.abs((idleWorking?.cssR ?? idleWorking?.attrR ?? 0) - 5.5) < 0.4,
|
|
98
|
+
transition_includes_r: (idleWorking?.transition || '').includes('r') &&
|
|
99
|
+
(idleWorking?.transition || '').includes('150ms'),
|
|
100
|
+
|
|
101
|
+
hov_working_state_hover: hovWorking?.state === 'hover',
|
|
102
|
+
hov_working_r_bigger: (hovWorking?.cssR ?? hovWorking?.attrR ?? 0) > 6.5,
|
|
103
|
+
hov_working_r_about_7: Math.abs((hovWorking?.cssR ?? hovWorking?.attrR ?? 0) - 7) < 0.4,
|
|
104
|
+
hov_idle_stays_idle: hovIdle?.state === 'idle',
|
|
105
|
+
hov_idle_r_unchanged: Math.abs((hovIdle?.cssR ?? hovIdle?.attrR ?? 0) - 5.5) < 0.4,
|
|
106
|
+
hov_offline_stays_idle: hovOffline?.state === 'idle',
|
|
107
|
+
|
|
108
|
+
settled_state_idle: settledWorking?.state === 'idle',
|
|
109
|
+
settled_r_back_small: Math.abs((settledWorking?.cssR ?? settledWorking?.attrR ?? 0) - 5.5) < 0.4,
|
|
110
|
+
};
|
|
111
|
+
const ok = Object.values(results).every(Boolean);
|
|
112
|
+
console.log(`${ok ? '✅' : '❌'} legend swatch grow:`, JSON.stringify(results),
|
|
113
|
+
`\n idle:`, idle,
|
|
114
|
+
`\n hovered:`, hovered,
|
|
115
|
+
`\n settled:`, settled);
|
|
116
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/* Round 295 verification: legend swatch idle radius 5.5 → 6.
|
|
2
|
+
*
|
|
3
|
+
* R197 grew the swatch on hover/pin from 5.5 → 7 (a 1.5px jump).
|
|
4
|
+
* R295 bumps the idle radius to 6 so the hover delta becomes
|
|
5
|
+
* a smoother 6→7 (1px lift). Idle reads slightly more like an
|
|
6
|
+
* authored color anchor than a faint dot.
|
|
7
|
+
*
|
|
8
|
+
* Contract:
|
|
9
|
+
* - All [data-legend-swatch] circles have attribute r='6' (was 5.5).
|
|
10
|
+
* - data-legend-swatch-state='idle' on all (no fixture hover).
|
|
11
|
+
* - Three swatches present (working / idle / offline).
|
|
12
|
+
* - R294 pulse dots absent + R290 three radar rings intact.
|
|
13
|
+
*/
|
|
14
|
+
import { chromium } from 'playwright';
|
|
15
|
+
import { readFileSync } from 'node:fs';
|
|
16
|
+
|
|
17
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
18
|
+
const browser = await chromium.launch({ headless: true });
|
|
19
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, 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 { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
23
|
+
});
|
|
24
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
25
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
26
|
+
const r = await route.fetch();
|
|
27
|
+
const b = await r.json();
|
|
28
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
29
|
+
const mk = (alias, model, status) => ({
|
|
30
|
+
alias, status, model, runtime: 'claude-code-cli',
|
|
31
|
+
network_id: nid, project_dir: null,
|
|
32
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
33
|
+
});
|
|
34
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
35
|
+
mk('alpha', 'claude-opus-4', 'working'),
|
|
36
|
+
mk('beta', 'gpt-4o', 'idle'),
|
|
37
|
+
mk('gamma', 'claude-sonnet-4', 'offline'),
|
|
38
|
+
] } });
|
|
39
|
+
});
|
|
40
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
41
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
42
|
+
|
|
43
|
+
const page = await ctx.newPage();
|
|
44
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
45
|
+
await page.waitForSelector('[data-legend-swatch]', { timeout: 15000 });
|
|
46
|
+
await page.waitForTimeout(300);
|
|
47
|
+
|
|
48
|
+
const probe = await page.evaluate(() => {
|
|
49
|
+
const swatches = [...document.querySelectorAll('[data-legend-swatch]')];
|
|
50
|
+
const samples = swatches.map(s => ({
|
|
51
|
+
key: s.getAttribute('data-legend-swatch'),
|
|
52
|
+
rAttr: s.getAttribute('r'),
|
|
53
|
+
rCss: getComputedStyle(s).r,
|
|
54
|
+
state: s.getAttribute('data-legend-swatch-state'),
|
|
55
|
+
}));
|
|
56
|
+
const pulses = document.querySelectorAll('[data-pulse-wrapper]');
|
|
57
|
+
const rings = [...document.querySelectorAll('[data-topo-radar-ring]')]
|
|
58
|
+
.map(r => r.getAttribute('data-topo-radar-ring')).sort((a, b) => +a - +b);
|
|
59
|
+
return {
|
|
60
|
+
samples,
|
|
61
|
+
pulseCount: pulses.length,
|
|
62
|
+
radarRings: rings,
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
await browser.close();
|
|
66
|
+
|
|
67
|
+
const results = {
|
|
68
|
+
three_swatches: probe.samples.length === 3,
|
|
69
|
+
all_idle_state: probe.samples.every(s => s.state === 'idle'),
|
|
70
|
+
all_radius_attr_6: probe.samples.every(s => s.rAttr === '6'),
|
|
71
|
+
all_computed_r_6: probe.samples.every(s => /^6/.test(s.rCss || '')),
|
|
72
|
+
r294_pulse_absent: probe.pulseCount === 0,
|
|
73
|
+
r290_three_rings: JSON.stringify(probe.radarRings) === JSON.stringify(['170', '250', '330']),
|
|
74
|
+
};
|
|
75
|
+
const ok = Object.values(results).every(Boolean);
|
|
76
|
+
console.log(`${ok ? '✅' : '❌'} legend swatch radius:`, JSON.stringify(results),
|
|
77
|
+
'\n swatch samples:', probe.samples);
|
|
78
|
+
process.exit(ok ? 0 : 1);
|