@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,108 @@
|
|
|
1
|
+
/* Round 158 verification: main canvas SVG gains aria-label +
|
|
2
|
+
* aria-roledescription.
|
|
3
|
+
*
|
|
4
|
+
* R151-R157 closed a11y across every interactive surface INSIDE
|
|
5
|
+
* the canvas (nodes / group labels / edge badges / recent rows /
|
|
6
|
+
* legend rows / chrome buttons / minimap). The canvas <svg> root
|
|
7
|
+
* itself was a nameless 1000×680 box — a screen-reader user who
|
|
8
|
+
* landed on it before tab-diving into children heard nothing
|
|
9
|
+
* identifying the surface.
|
|
10
|
+
*
|
|
11
|
+
* R158 closes that gap:
|
|
12
|
+
* aria-roledescription="agent network topology"
|
|
13
|
+
* aria-label live-updates with snapshot:
|
|
14
|
+
* "Agent network topology — 5 agents online · 3 working ·
|
|
15
|
+
* 1 offline · 2 active links. Tab to navigate nodes,
|
|
16
|
+
* double-click canvas to reset view."
|
|
17
|
+
*
|
|
18
|
+
* No role override — default SVG role (graphics-document) keeps
|
|
19
|
+
* children navigable; role="img" would flatten the SVG to a
|
|
20
|
+
* single opaque image and hide all the R151-R157 work.
|
|
21
|
+
*
|
|
22
|
+
* Test:
|
|
23
|
+
* 1. Mock 3 working + 2 idle online + 1 offline + 2 active links
|
|
24
|
+
* 2. Assert canvas SVG has both ARIA attrs
|
|
25
|
+
* 3. Assert aria-label contains all four count fragments + gesture hints
|
|
26
|
+
* 4. Assert no role="img" (would defeat children a11y)
|
|
27
|
+
*/
|
|
28
|
+
import { chromium } from 'playwright';
|
|
29
|
+
import { readFileSync } from 'node:fs';
|
|
30
|
+
|
|
31
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
32
|
+
const browser = await chromium.launch({ headless: true });
|
|
33
|
+
|
|
34
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
35
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
36
|
+
await ctx.addInitScript(() => {
|
|
37
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
41
|
+
const stale = new Date(Date.now() - 10 * 60 * 1000).toISOString();
|
|
42
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
43
|
+
const r = await route.fetch();
|
|
44
|
+
const b = await r.json();
|
|
45
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
46
|
+
const mk = (alias, status, last) => ({
|
|
47
|
+
alias, status, model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
48
|
+
network_id: nid, project_dir: null,
|
|
49
|
+
created_at: last, updated_at: last, last_seen_at: last,
|
|
50
|
+
});
|
|
51
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
52
|
+
mk('agents-a1', 'working', fresh),
|
|
53
|
+
mk('agents-a2', 'working', fresh),
|
|
54
|
+
mk('agents-a3', 'working', fresh),
|
|
55
|
+
mk('infra-b1', 'idle', fresh),
|
|
56
|
+
mk('infra-b2', 'idle', fresh),
|
|
57
|
+
mk('legacy-c1', 'offline', stale),
|
|
58
|
+
] } });
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const now = Date.now();
|
|
62
|
+
// 4 a1↔a2 msgs (R100 threshold for edge badge) + 1 a2↔a3 = 2 flowLinks
|
|
63
|
+
const msgs = [];
|
|
64
|
+
for (let i = 0; i < 4; i++) {
|
|
65
|
+
msgs.push({ id: `m${i}`, from_alias: 'agents-a1', to_alias: 'agents-a2', content: 'hi',
|
|
66
|
+
network_id: 'default', created_at: new Date(now - (10000 + i * 500)).toISOString() });
|
|
67
|
+
}
|
|
68
|
+
msgs.push({ id: 'mx', from_alias: 'agents-a2', to_alias: 'agents-a3', content: 'hi',
|
|
69
|
+
network_id: 'default', created_at: new Date(now - 14000).toISOString() });
|
|
70
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
|
|
71
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
72
|
+
|
|
73
|
+
const page = await ctx.newPage();
|
|
74
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
75
|
+
// 5 online + 1 offline = 6 g[data-node]
|
|
76
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 6, { timeout: 30000 });
|
|
77
|
+
await page.waitForTimeout(400);
|
|
78
|
+
|
|
79
|
+
const probe = await page.evaluate(() => {
|
|
80
|
+
const el = document.querySelector('[data-topo-canvas-aria]');
|
|
81
|
+
if (!el) return null;
|
|
82
|
+
return {
|
|
83
|
+
tagName: el.tagName.toLowerCase(),
|
|
84
|
+
role: el.getAttribute('role'),
|
|
85
|
+
roledesc: el.getAttribute('aria-roledescription'),
|
|
86
|
+
label: el.getAttribute('aria-label'),
|
|
87
|
+
};
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
await browser.close();
|
|
91
|
+
|
|
92
|
+
const label = probe?.label || '';
|
|
93
|
+
const results = {
|
|
94
|
+
canvas_found: probe !== null,
|
|
95
|
+
is_svg: probe?.tagName === 'svg',
|
|
96
|
+
no_role_img: probe?.role !== 'img',
|
|
97
|
+
has_roledescription: probe?.roledesc === 'agent network topology',
|
|
98
|
+
label_mentions_online: /5 agents online/.test(label),
|
|
99
|
+
label_mentions_working: /3 working/.test(label),
|
|
100
|
+
label_mentions_offline: /1 offline/.test(label),
|
|
101
|
+
label_mentions_links: /2 active links/.test(label),
|
|
102
|
+
label_mentions_tab: /Tab to navigate/.test(label),
|
|
103
|
+
label_mentions_doubleclick: /double-click/.test(label),
|
|
104
|
+
};
|
|
105
|
+
const ok = Object.values(results).every(Boolean);
|
|
106
|
+
console.log(`${ok ? '✅' : '❌'} canvas SVG aria:`, JSON.stringify(results),
|
|
107
|
+
`\n probe=`, probe);
|
|
108
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/* Round 327 verification: canvas brand crescent (top-left corner,
|
|
2
|
+
* codex-shipped via 1817f55 Hero 3 §3.I) joins the always-mount-
|
|
3
|
+
* opacity-gate family. Pre-R327 it conditionally mounted on
|
|
4
|
+
* `flowLinks.length === 0` — first flow arriving SNAP-removed it.
|
|
5
|
+
* Now always-mounted with opacity={flowLinks.length === 0 ? 0.35 : 0}
|
|
6
|
+
* + transition opacity 300ms ease-out so the crescent crossfades
|
|
7
|
+
* in/out as the recent-signal panel state flips.
|
|
8
|
+
*
|
|
9
|
+
* 11th surface in the always-mount-opacity-gate idiom family
|
|
10
|
+
* (R181/R182/R183/R213×2/R214/R215/R221/R222/R223/R327).
|
|
11
|
+
*
|
|
12
|
+
* Contract:
|
|
13
|
+
* - [data-topo-brand-canvas-mark] is present (always-mounted).
|
|
14
|
+
* - When flowLinks.length === 0 (no messages mocked): opacity=0.35
|
|
15
|
+
* + data-topo-brand-canvas-mark-visible='true'.
|
|
16
|
+
* - Computed style.transition contains 'opacity'.
|
|
17
|
+
* - R326 chrome gap-2 + R317/R318 chrome regressions intact.
|
|
18
|
+
* - R294 pulse absent.
|
|
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
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
26
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
27
|
+
await ctx.addInitScript(() => {
|
|
28
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
29
|
+
});
|
|
30
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
31
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
32
|
+
const r = await route.fetch();
|
|
33
|
+
const b = await r.json();
|
|
34
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
35
|
+
const mk = (alias) => ({
|
|
36
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
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: [ mk('alpha'), mk('beta') ] } });
|
|
41
|
+
});
|
|
42
|
+
// Zero messages: recent-signal panel hidden, brand crescent visible.
|
|
43
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
44
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
45
|
+
|
|
46
|
+
const page = await ctx.newPage();
|
|
47
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
48
|
+
await page.waitForSelector('[data-topo-brand-canvas-mark]', { timeout: 15000 });
|
|
49
|
+
await page.waitForTimeout(400);
|
|
50
|
+
|
|
51
|
+
const probe = await page.evaluate(() => {
|
|
52
|
+
const mark = document.querySelector('[data-topo-brand-canvas-mark]');
|
|
53
|
+
const chrome = document.querySelector('[data-topo-chrome]');
|
|
54
|
+
return {
|
|
55
|
+
markPresent: mark !== null,
|
|
56
|
+
markOpacityAttr: mark?.getAttribute('opacity') ?? null,
|
|
57
|
+
markVisibleAttr: mark?.getAttribute('data-topo-brand-canvas-mark-visible') ?? null,
|
|
58
|
+
markTransition: mark ? getComputedStyle(mark).transition : null,
|
|
59
|
+
// R326 regression: chrome strip gap-2.
|
|
60
|
+
chromeGap: chrome ? getComputedStyle(chrome).columnGap : null,
|
|
61
|
+
layoutInactiveCls: document.querySelector('[data-topo-chrome-layout="grid"]')?.className ?? '',
|
|
62
|
+
layoutActiveCls: document.querySelector('[data-topo-chrome-layout="ring"]')?.className ?? '',
|
|
63
|
+
pulseCount: document.querySelectorAll('[data-pulse-wrapper]').length,
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
await browser.close();
|
|
67
|
+
|
|
68
|
+
const hasOpacityTransition = (s) => /opacity\s+0?\.?\d*s|opacity\s+\d+ms/i.test(s || '');
|
|
69
|
+
|
|
70
|
+
const results = {
|
|
71
|
+
mark_present: probe.markPresent,
|
|
72
|
+
mark_visible_when_empty: probe.markVisibleAttr === 'true',
|
|
73
|
+
mark_opacity_0_35: probe.markOpacityAttr === '0.35',
|
|
74
|
+
mark_has_opacity_trans: hasOpacityTransition(probe.markTransition),
|
|
75
|
+
// R326 regression.
|
|
76
|
+
r326_chrome_gap_8px: probe.chromeGap === '8px',
|
|
77
|
+
// R317 / R318 chrome regression.
|
|
78
|
+
r317_inactive_gray_400: probe.layoutInactiveCls.includes('text-gray-400'),
|
|
79
|
+
r318_active_font_medium: probe.layoutActiveCls.includes('font-medium'),
|
|
80
|
+
r294_pulse_absent: probe.pulseCount === 0,
|
|
81
|
+
};
|
|
82
|
+
const ok = Object.values(results).every(Boolean);
|
|
83
|
+
console.log(`${ok ? '✅' : '❌'} canvas-brand crescent fade:`, JSON.stringify(results),
|
|
84
|
+
'\n opacity attr:', probe.markOpacityAttr,
|
|
85
|
+
'\n visible attr:', probe.markVisibleAttr,
|
|
86
|
+
'\n transition:', probe.markTransition);
|
|
87
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/* Round 120 verification: chat-target ring breathes when chat is
|
|
2
|
+
* open. R51 added the static ring at radius+14; R120 adds a SMIL
|
|
3
|
+
* opacity pulse (±0.1, 3s) so the active chat session reads as
|
|
4
|
+
* "live" without being intrusive. Gated by reducedMotion.
|
|
5
|
+
*
|
|
6
|
+
* Drive chatAlias by clicking a node to open the popover. Verify:
|
|
7
|
+
* - chat-target ring renders with data-chat-target-breath="on"
|
|
8
|
+
* - the ring has an <animate> child with dur="3s"
|
|
9
|
+
* - reducedMotion suppresses the breath
|
|
10
|
+
*/
|
|
11
|
+
import { chromium } from 'playwright';
|
|
12
|
+
import { readFileSync } from 'node:fs';
|
|
13
|
+
|
|
14
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
15
|
+
const browser = await chromium.launch({ headless: true });
|
|
16
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
17
|
+
|
|
18
|
+
const probe = async (reducedMotion) => {
|
|
19
|
+
const ctx = await browser.newContext({
|
|
20
|
+
viewport: { width: 1400, height: 1500 },
|
|
21
|
+
reducedMotion: reducedMotion ? 'reduce' : 'no-preference',
|
|
22
|
+
});
|
|
23
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
24
|
+
await ctx.addInitScript(() => {
|
|
25
|
+
try {
|
|
26
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
27
|
+
localStorage.setItem('anet-topo-layout', 'ring');
|
|
28
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
29
|
+
} catch {}
|
|
30
|
+
});
|
|
31
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
32
|
+
const r = await route.fetch();
|
|
33
|
+
const b = await r.json();
|
|
34
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
35
|
+
const mk = (alias) => ({
|
|
36
|
+
alias, status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
37
|
+
network_id: nid, project_dir: null,
|
|
38
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
39
|
+
});
|
|
40
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta')] } });
|
|
41
|
+
});
|
|
42
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
43
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
44
|
+
const page = await ctx.newPage();
|
|
45
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
46
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 2, { timeout: 30000 });
|
|
47
|
+
await page.waitForTimeout(400);
|
|
48
|
+
|
|
49
|
+
// Open chat by clicking the alpha node.
|
|
50
|
+
await page.locator('g[data-node="alpha"]').click();
|
|
51
|
+
await page.waitForTimeout(500);
|
|
52
|
+
|
|
53
|
+
const data = await page.evaluate(() => {
|
|
54
|
+
const ring = document.querySelector('[data-chat-target-ring]');
|
|
55
|
+
const anim = ring?.querySelector('animate');
|
|
56
|
+
return {
|
|
57
|
+
ringPresent: !!ring,
|
|
58
|
+
breathAttr: ring?.getAttribute('data-chat-target-breath') || '',
|
|
59
|
+
hasAnimate: !!anim,
|
|
60
|
+
dur: anim?.getAttribute('dur') || null,
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
await ctx.close();
|
|
64
|
+
return data;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const normal = await probe(false);
|
|
68
|
+
const reduced = await probe(true);
|
|
69
|
+
await browser.close();
|
|
70
|
+
|
|
71
|
+
const results = {
|
|
72
|
+
normal_ringPresent: normal.ringPresent === true,
|
|
73
|
+
normal_breathOn: normal.breathAttr === 'on',
|
|
74
|
+
normal_hasAnimate: normal.hasAnimate === true,
|
|
75
|
+
normal_dur3s: normal.dur === '3s',
|
|
76
|
+
reduced_ringPresent: reduced.ringPresent === true,
|
|
77
|
+
reduced_breathOff: reduced.breathAttr === 'off',
|
|
78
|
+
reduced_noAnimate: reduced.hasAnimate === false,
|
|
79
|
+
};
|
|
80
|
+
const ok = Object.values(results).every(Boolean);
|
|
81
|
+
console.log(`${ok ? '✅' : '❌'} chat-target breath:`, JSON.stringify(results),
|
|
82
|
+
`\n normal=`, normal,
|
|
83
|
+
`\n reduced=`, reduced);
|
|
84
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/* Round 242 verification: chat-target ring (R183) transition list
|
|
2
|
+
* grows to include stroke + filter alongside the existing opacity
|
|
3
|
+
* gate, so a chat-target node's status flips (green→teal etc.) and
|
|
4
|
+
* the glow toggle (cyber theme) both ease over 200ms instead of
|
|
5
|
+
* snapping.
|
|
6
|
+
*
|
|
7
|
+
* The ring is always-mounted (R183 idiom) with opacity gated by
|
|
8
|
+
* isChat. Without setting a chat target, opacity stays at 0 — but
|
|
9
|
+
* the transition wiring is in style.transition regardless, so we
|
|
10
|
+
* can probe it without needing to demonstrate the easing live.
|
|
11
|
+
*
|
|
12
|
+
* Test scope per ring:
|
|
13
|
+
* - data-chat-target-ring attr present (probe entry)
|
|
14
|
+
* - data-chat-target-active='false' (no chat at default state)
|
|
15
|
+
* - style.transition includes opacity / stroke / filter
|
|
16
|
+
* each at 200ms (or 0.2s browser-normalised)
|
|
17
|
+
* - stroke attribute present (color from status.primary)
|
|
18
|
+
* - opacity attribute = 0 (gated by isChat=false)
|
|
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
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
26
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
27
|
+
await ctx.addInitScript(() => {
|
|
28
|
+
try {
|
|
29
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
30
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
31
|
+
localStorage.setItem('anet-topo-layout', 'ring');
|
|
32
|
+
} catch {}
|
|
33
|
+
});
|
|
34
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
35
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
36
|
+
const r = await route.fetch();
|
|
37
|
+
const b = await r.json();
|
|
38
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
39
|
+
const mk = (alias) => ({
|
|
40
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
41
|
+
network_id: nid, project_dir: null,
|
|
42
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
43
|
+
});
|
|
44
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
45
|
+
mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
|
|
46
|
+
] } });
|
|
47
|
+
});
|
|
48
|
+
await ctx.route('**/api/hub/messages*', (r2) => r2.fulfill({ json: { messages: [] } }));
|
|
49
|
+
await ctx.route('**/api/hub/tasks*', (r2) => r2.fulfill({ json: { tasks: [] } }));
|
|
50
|
+
|
|
51
|
+
const page = await ctx.newPage();
|
|
52
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
53
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
54
|
+
await page.waitForSelector('[data-chat-target-ring]', { timeout: 10000, state: 'attached' });
|
|
55
|
+
await page.waitForTimeout(300);
|
|
56
|
+
|
|
57
|
+
const probe = await page.evaluate(() => {
|
|
58
|
+
const rings = Array.from(document.querySelectorAll('[data-chat-target-ring]'));
|
|
59
|
+
return rings.map((r) => ({
|
|
60
|
+
active: r.getAttribute('data-chat-target-active'),
|
|
61
|
+
stroke: r.getAttribute('stroke'),
|
|
62
|
+
opacity: r.getAttribute('opacity'),
|
|
63
|
+
transition: r.style.transition,
|
|
64
|
+
}));
|
|
65
|
+
});
|
|
66
|
+
await browser.close();
|
|
67
|
+
|
|
68
|
+
const hasProp = (s, prop) => new RegExp(`${prop}\\s+(?:200ms|0\\.2s)`).test(s || '');
|
|
69
|
+
|
|
70
|
+
const results = {
|
|
71
|
+
four_rings: probe.length === 4,
|
|
72
|
+
all_inactive: probe.every(r => r.active === 'false'),
|
|
73
|
+
all_have_stroke: probe.every(r => typeof r.stroke === 'string' && r.stroke.length > 0),
|
|
74
|
+
all_opacity_zero: probe.every(r => r.opacity === '0'),
|
|
75
|
+
all_transition_has_opacity: probe.every(r => hasProp(r.transition, 'opacity')),
|
|
76
|
+
all_transition_has_stroke: probe.every(r => hasProp(r.transition, 'stroke')),
|
|
77
|
+
all_transition_has_filter: probe.every(r => hasProp(r.transition, 'filter')),
|
|
78
|
+
};
|
|
79
|
+
const ok = Object.values(results).every(Boolean);
|
|
80
|
+
console.log(`${ok ? '✅' : '❌'} chat-target ring ease:`, JSON.stringify(results),
|
|
81
|
+
'\n rings:', probe.map(r => ({ a: r.active, stroke: r.stroke, t: r.transition.slice(0, 80) })));
|
|
82
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/* Round 183 verification: chat-target ring (R11/R120) fades in/out
|
|
2
|
+
* instead of mount/unmount.
|
|
3
|
+
*
|
|
4
|
+
* Pre-R183 the ring was conditionally mounted on
|
|
5
|
+
* `chatAlias === session.alias`; the className `transition-opacity
|
|
6
|
+
* duration-200` never fired because the element didn't exist
|
|
7
|
+
* before mount. R183 makes the ring always-mounted with opacity
|
|
8
|
+
* gated by isChat — the CSS transition now fires cleanly on
|
|
9
|
+
* chat-close. The SMIL <animate> stays gated by
|
|
10
|
+
* `!reducedMotion && isChat` so it only runs for the active
|
|
11
|
+
* chat target (otherwise SMIL on every node would fight the
|
|
12
|
+
* opacity transition globally).
|
|
13
|
+
*
|
|
14
|
+
* Test:
|
|
15
|
+
* 1. Mock 2 sessions (alpha, beta)
|
|
16
|
+
* 2. Idle: both rings always-mounted, opacity=0, active=false
|
|
17
|
+
* 3. Click alpha → alpha opacity=0.95 active=true,
|
|
18
|
+
* beta stays opacity=0 active=false
|
|
19
|
+
* 4. Click beta → beta opacity=0.95 active=true,
|
|
20
|
+
* alpha back to 0 (chatAlias swap)
|
|
21
|
+
* 5. All rings carry 'opacity 200ms ease-out' transition
|
|
22
|
+
* 6. strokeWidth='2.5' (R51 sentinel-safe)
|
|
23
|
+
*/
|
|
24
|
+
import { chromium } from 'playwright';
|
|
25
|
+
import { readFileSync } from 'node:fs';
|
|
26
|
+
|
|
27
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
28
|
+
const browser = await chromium.launch({ headless: true });
|
|
29
|
+
|
|
30
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
31
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
32
|
+
await ctx.addInitScript(() => {
|
|
33
|
+
try {
|
|
34
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
35
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
36
|
+
localStorage.setItem('anet-topo-layout', 'ring');
|
|
37
|
+
} catch {}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
41
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
42
|
+
const r = await route.fetch();
|
|
43
|
+
const b = await r.json();
|
|
44
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
45
|
+
const mk = (alias) => ({
|
|
46
|
+
alias, status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
47
|
+
network_id: nid, project_dir: null,
|
|
48
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
49
|
+
});
|
|
50
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta')] } });
|
|
51
|
+
});
|
|
52
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
53
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
54
|
+
|
|
55
|
+
const page = await ctx.newPage();
|
|
56
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
57
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 2, { timeout: 30000 });
|
|
58
|
+
await page.waitForSelector('[data-chat-target-ring]', { timeout: 10000 });
|
|
59
|
+
await page.waitForTimeout(400);
|
|
60
|
+
|
|
61
|
+
const probe = () => page.evaluate(() => {
|
|
62
|
+
const rings = [...document.querySelectorAll('g[data-node] [data-chat-target-ring]')];
|
|
63
|
+
return rings.map(r => {
|
|
64
|
+
const node = r.closest('g[data-node]');
|
|
65
|
+
return {
|
|
66
|
+
alias: node?.getAttribute('data-node'),
|
|
67
|
+
opacity: parseFloat(r.getAttribute('opacity') || ''),
|
|
68
|
+
active: r.getAttribute('data-chat-target-active'),
|
|
69
|
+
breath: r.getAttribute('data-chat-target-breath'),
|
|
70
|
+
strokeWidth: r.getAttribute('stroke-width'),
|
|
71
|
+
transition: r.style.transition || getComputedStyle(r).transition,
|
|
72
|
+
hasAnimate: !!r.querySelector('animate'),
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const idle = await probe();
|
|
78
|
+
|
|
79
|
+
await page.locator('g[data-node="alpha"]').click();
|
|
80
|
+
await page.waitForTimeout(400);
|
|
81
|
+
const onAlpha = await probe();
|
|
82
|
+
|
|
83
|
+
await page.locator('g[data-node="beta"]').click();
|
|
84
|
+
await page.waitForTimeout(400);
|
|
85
|
+
const onBeta = await probe();
|
|
86
|
+
|
|
87
|
+
await browser.close();
|
|
88
|
+
|
|
89
|
+
const findRing = (rings, alias) => rings.find(r => r.alias === alias);
|
|
90
|
+
const hasTransition = (s) =>
|
|
91
|
+
(s.transition || '').includes('opacity 200ms') ||
|
|
92
|
+
/opacity\s+0\.2s|opacity\s+200ms/.test(s.transition || '');
|
|
93
|
+
|
|
94
|
+
const results = {
|
|
95
|
+
two_rings_present: idle.length === 2,
|
|
96
|
+
all_strokewidth_2p5: idle.every(r => r.strokeWidth === '2.5'),
|
|
97
|
+
all_have_transition: idle.every(r => hasTransition(r)),
|
|
98
|
+
|
|
99
|
+
idle_all_opacity_0: idle.every(r => r.opacity === 0),
|
|
100
|
+
idle_all_inactive: idle.every(r => r.active === 'false'),
|
|
101
|
+
idle_none_have_animate: idle.every(r => r.hasAnimate === false),
|
|
102
|
+
|
|
103
|
+
// Click alpha → alpha active, beta inactive
|
|
104
|
+
alpha_active_opacity_0p95: Math.abs((findRing(onAlpha, 'alpha')?.opacity ?? 0) - 0.95) < 0.05,
|
|
105
|
+
alpha_active_attr: findRing(onAlpha, 'alpha')?.active === 'true',
|
|
106
|
+
alpha_breath_on: findRing(onAlpha, 'alpha')?.breath === 'on',
|
|
107
|
+
alpha_has_animate: findRing(onAlpha, 'alpha')?.hasAnimate === true,
|
|
108
|
+
beta_inactive_opacity_0: findRing(onAlpha, 'beta')?.opacity === 0,
|
|
109
|
+
beta_inactive_attr: findRing(onAlpha, 'beta')?.active === 'false',
|
|
110
|
+
beta_breath_off: findRing(onAlpha, 'beta')?.breath === 'off',
|
|
111
|
+
beta_no_animate: findRing(onAlpha, 'beta')?.hasAnimate === false,
|
|
112
|
+
|
|
113
|
+
// Click beta → swap; alpha back to 0, beta active
|
|
114
|
+
beta_active_opacity_0p95: Math.abs((findRing(onBeta, 'beta')?.opacity ?? 0) - 0.95) < 0.05,
|
|
115
|
+
beta_active_attr: findRing(onBeta, 'beta')?.active === 'true',
|
|
116
|
+
alpha_back_to_0: findRing(onBeta, 'alpha')?.opacity === 0,
|
|
117
|
+
alpha_back_to_inactive: findRing(onBeta, 'alpha')?.active === 'false',
|
|
118
|
+
};
|
|
119
|
+
const ok = Object.values(results).every(Boolean);
|
|
120
|
+
console.log(`${ok ? '✅' : '❌'} chat-target ring fade:`, JSON.stringify(results),
|
|
121
|
+
`\n idle:`, idle,
|
|
122
|
+
`\n on alpha:`, onAlpha,
|
|
123
|
+
`\n on beta:`, onBeta);
|
|
124
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/* Round 205 verification: working / online chip-row chips visually
|
|
2
|
+
* recede to opacity 0.5 when their tier is empty, mirroring R204's
|
|
3
|
+
* legend count recede pattern at the chip-row scope.
|
|
4
|
+
*
|
|
5
|
+
* Pre-R205 "0 working" displayed at the same bg-green-500/10 +
|
|
6
|
+
* full opacity as "12 working" — the eye got zero signal that the
|
|
7
|
+
* tier was empty. R205 combines with R139's clickable=false +
|
|
8
|
+
* tooltip-undefined: visual + interactive + affordance all say
|
|
9
|
+
* "nothing to act on".
|
|
10
|
+
*
|
|
11
|
+
* Test scenarios:
|
|
12
|
+
* A. All offline (0 working, 0 online):
|
|
13
|
+
* working chip empty='true', opacity ≈ 0.5
|
|
14
|
+
* online chip empty='true', opacity ≈ 0.5
|
|
15
|
+
* B. All working (4 working, 4 online):
|
|
16
|
+
* working chip empty='false', opacity = 1
|
|
17
|
+
* online chip empty='false', opacity = 1
|
|
18
|
+
* Both: inline transition includes 'opacity 200ms' alongside the
|
|
19
|
+
* existing R180 box-shadow / R201 bg/border transitions.
|
|
20
|
+
*/
|
|
21
|
+
import { chromium } from 'playwright';
|
|
22
|
+
import { readFileSync } from 'node:fs';
|
|
23
|
+
|
|
24
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
25
|
+
const browser = await chromium.launch({ headless: true });
|
|
26
|
+
|
|
27
|
+
async function pageFor(allStatus) {
|
|
28
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
29
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
30
|
+
await ctx.addInitScript(() => {
|
|
31
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
32
|
+
});
|
|
33
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
34
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
35
|
+
const r = await route.fetch();
|
|
36
|
+
const b = await r.json();
|
|
37
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
38
|
+
const mk = (alias, status) => ({
|
|
39
|
+
alias, status, model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
40
|
+
network_id: nid, project_dir: null,
|
|
41
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
42
|
+
});
|
|
43
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
44
|
+
mk('alpha', allStatus), mk('beta', allStatus),
|
|
45
|
+
mk('gamma', allStatus), mk('delta', allStatus),
|
|
46
|
+
] } });
|
|
47
|
+
});
|
|
48
|
+
await ctx.route('**/api/hub/messages*', (r2) => r2.fulfill({ json: { messages: [] } }));
|
|
49
|
+
await ctx.route('**/api/hub/tasks*', (r2) => r2.fulfill({ json: { tasks: [] } }));
|
|
50
|
+
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.waitForSelector('[data-working-chip]', { timeout: 10000 });
|
|
54
|
+
await page.waitForTimeout(300);
|
|
55
|
+
return page;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const probe = async (page) => page.evaluate(() => {
|
|
59
|
+
function pick(sel, emptyKey) {
|
|
60
|
+
const el = document.querySelector(sel);
|
|
61
|
+
if (!el) return null;
|
|
62
|
+
const cs = getComputedStyle(el);
|
|
63
|
+
return {
|
|
64
|
+
empty: el.getAttribute(emptyKey),
|
|
65
|
+
opacity: parseFloat(cs.opacity),
|
|
66
|
+
transition: el.style.transition,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
working: pick('[data-working-chip]', 'data-working-chip-empty'),
|
|
71
|
+
online: pick('[data-online-chip]', 'data-online-chip-empty'),
|
|
72
|
+
};
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Scenario A: all offline → both tiers empty
|
|
76
|
+
const pageA = await pageFor('offline');
|
|
77
|
+
const probeA = await probe(pageA);
|
|
78
|
+
await pageA.close();
|
|
79
|
+
|
|
80
|
+
// Scenario B: all working → both tiers populated
|
|
81
|
+
const pageB = await pageFor('working');
|
|
82
|
+
const probeB = await probe(pageB);
|
|
83
|
+
await pageB.close();
|
|
84
|
+
await browser.close();
|
|
85
|
+
|
|
86
|
+
const near = (got, want, tol = 0.05) => Math.abs(got - want) < tol;
|
|
87
|
+
const hasOpacityTransition = (s) => /opacity\s+(?:200ms|0\.2s)/.test(s || '');
|
|
88
|
+
const hasBgTransition = (s) => /background-color\s+(?:200ms|0\.2s)/.test(s || '');
|
|
89
|
+
const hasBoxShadowTransition = (s) => /box-shadow\s+(?:150ms|0\.15s)/.test(s || '');
|
|
90
|
+
|
|
91
|
+
const results = {
|
|
92
|
+
// Scenario A: all offline
|
|
93
|
+
A_working_empty_attr: probeA.working?.empty === 'true',
|
|
94
|
+
A_working_opacity_half: near(probeA.working?.opacity ?? 0, 0.5),
|
|
95
|
+
A_online_empty_attr: probeA.online?.empty === 'true',
|
|
96
|
+
A_online_opacity_half: near(probeA.online?.opacity ?? 0, 0.5),
|
|
97
|
+
|
|
98
|
+
// Scenario B: all working
|
|
99
|
+
B_working_empty_false: probeB.working?.empty === 'false',
|
|
100
|
+
B_working_opacity_full: near(probeB.working?.opacity ?? 0, 1.0),
|
|
101
|
+
B_online_empty_false: probeB.online?.empty === 'false',
|
|
102
|
+
B_online_opacity_full: near(probeB.online?.opacity ?? 0, 1.0),
|
|
103
|
+
|
|
104
|
+
// Transition list integrity — opacity ADDED, bg/border/box-shadow KEPT
|
|
105
|
+
A_working_transition_opacity: hasOpacityTransition(probeA.working?.transition),
|
|
106
|
+
A_online_transition_opacity: hasOpacityTransition(probeA.online?.transition),
|
|
107
|
+
B_working_transition_opacity: hasOpacityTransition(probeB.working?.transition),
|
|
108
|
+
B_online_transition_opacity: hasOpacityTransition(probeB.online?.transition),
|
|
109
|
+
A_working_keeps_bg_transition: hasBgTransition(probeA.working?.transition),
|
|
110
|
+
A_working_keeps_bs_transition: hasBoxShadowTransition(probeA.working?.transition),
|
|
111
|
+
};
|
|
112
|
+
const ok = Object.values(results).every(Boolean);
|
|
113
|
+
console.log(`${ok ? '✅' : '❌'} chip empty recede:`, JSON.stringify(results),
|
|
114
|
+
`\n probeA (offline):`, probeA,
|
|
115
|
+
`\n probeB (working):`, probeB);
|
|
116
|
+
process.exit(ok ? 0 : 1);
|