@sleep2agi/agent-network-dashboard 0.4.6-preview.99 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_ID +1 -1
- package/.next/app-path-routes-manifest.json +2 -0
- package/.next/build-manifest.json +3 -3
- package/.next/diagnostics/route-bundle-stats.json +31 -31
- package/.next/fallback-build-manifest.json +3 -3
- package/.next/prerender-manifest.json +3 -3
- package/.next/routes-manifest.json +16 -0
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +2 -2
- package/.next/server/app/_not-found.rsc +12 -12
- package/.next/server/app/_not-found.segments/_full.segment.rsc +12 -12
- package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/server/app/_not-found.segments/_index.segment.rsc +7 -7
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
- package/.next/server/app/admin.html +2 -2
- package/.next/server/app/admin.rsc +14 -14
- package/.next/server/app/admin.segments/_full.segment.rsc +14 -14
- package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
- package/.next/server/app/admin.segments/_index.segment.rsc +7 -7
- package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
- package/.next/server/app/api/hub/server/[hostname]/agents/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/hub/server/[hostname]/agents/route/build-manifest.json +9 -0
- package/.next/server/app/api/hub/server/[hostname]/agents/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/hub/server/[hostname]/agents/route.js +7 -0
- package/.next/server/app/api/hub/server/[hostname]/agents/route.js.map +5 -0
- package/.next/server/app/api/hub/server/[hostname]/agents/route.js.nft.json +1 -0
- package/.next/server/app/api/hub/server/[hostname]/agents/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/hub/server/[hostname]/health/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/hub/server/[hostname]/health/route/build-manifest.json +9 -0
- package/.next/server/app/api/hub/server/[hostname]/health/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/hub/server/[hostname]/health/route.js +7 -0
- package/.next/server/app/api/hub/server/[hostname]/health/route.js.map +5 -0
- package/.next/server/app/api/hub/server/[hostname]/health/route.js.nft.json +1 -0
- package/.next/server/app/api/hub/server/[hostname]/health/route_client-reference-manifest.js +3 -0
- package/.next/server/app/index.html +2 -2
- package/.next/server/app/index.rsc +14 -14
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/index.segments/_full.segment.rsc +14 -14
- package/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/server/app/index.segments/_index.segment.rsc +7 -7
- package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/login.html +2 -2
- package/.next/server/app/login.rsc +14 -14
- package/.next/server/app/login.segments/_full.segment.rsc +14 -14
- package/.next/server/app/login.segments/_head.segment.rsc +4 -4
- package/.next/server/app/login.segments/_index.segment.rsc +7 -7
- package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/login.segments/login.segment.rsc +3 -3
- package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/logs.html +2 -2
- package/.next/server/app/logs.rsc +14 -14
- package/.next/server/app/logs.segments/_full.segment.rsc +14 -14
- package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/logs.segments/_index.segment.rsc +7 -7
- package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
- package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
- package/.next/server/app/messages.html +2 -2
- package/.next/server/app/messages.rsc +14 -14
- package/.next/server/app/messages.segments/_full.segment.rsc +14 -14
- package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
- package/.next/server/app/messages.segments/_index.segment.rsc +7 -7
- package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
- package/.next/server/app/node/page_client-reference-manifest.js +1 -1
- package/.next/server/app/node.html +2 -2
- package/.next/server/app/node.rsc +14 -14
- package/.next/server/app/node.segments/_full.segment.rsc +14 -14
- package/.next/server/app/node.segments/_head.segment.rsc +4 -4
- package/.next/server/app/node.segments/_index.segment.rsc +7 -7
- package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/node.segments/node.segment.rsc +3 -3
- package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
- package/.next/server/app/nodes.html +2 -2
- package/.next/server/app/nodes.rsc +14 -14
- package/.next/server/app/nodes.segments/_full.segment.rsc +14 -14
- package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
- package/.next/server/app/nodes.segments/_index.segment.rsc +7 -7
- package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/server-logs.html +2 -2
- package/.next/server/app/server-logs.rsc +14 -14
- package/.next/server/app/server-logs.segments/_full.segment.rsc +14 -14
- package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/server-logs.segments/_index.segment.rsc +7 -7
- package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
- package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/networks.html +2 -2
- package/.next/server/app/settings/networks.rsc +14 -14
- package/.next/server/app/settings/networks.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/networks.segments/_index.segment.rsc +7 -7
- package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
- package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
- package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens.html +2 -2
- package/.next/server/app/settings/tokens.rsc +14 -14
- package/.next/server/app/settings/tokens.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/tokens.segments/_index.segment.rsc +7 -7
- package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
- package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
- package/.next/server/app/settings.html +2 -2
- package/.next/server/app/settings.rsc +14 -14
- package/.next/server/app/settings.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings.segments/_index.segment.rsc +7 -7
- package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
- package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
- package/.next/server/app/tasks.html +2 -2
- package/.next/server/app/tasks.rsc +14 -14
- package/.next/server/app/tasks.segments/_full.segment.rsc +14 -14
- package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/tasks.segments/_index.segment.rsc +7 -7
- package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
- package/.next/server/app-paths-manifest.json +2 -0
- package/.next/server/chunks/0dk5_next-internal_server_app_api_hub_server_[hostname]_agents_route_actions_0vr92s4.js +3 -0
- package/.next/server/chunks/0dk5_next-internal_server_app_api_hub_server_[hostname]_agents_route_actions_0vr92s4.js.map +1 -0
- package/.next/server/chunks/0dk5_next-internal_server_app_api_hub_server_[hostname]_health_route_actions_0vr-bv5.js +3 -0
- package/.next/server/chunks/0dk5_next-internal_server_app_api_hub_server_[hostname]_health_route_actions_0vr-bv5.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__05nyivw._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__05nyivw._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0floe3h._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0floe3h._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +4 -2
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js.map +1 -1
- package/.next/server/middleware-build-manifest.js +3 -3
- package/.next/server/pages/404.html +2 -2
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.js +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/0aauz~36q5n2a.css +2 -0
- package/.next/static/chunks/0f~0kgy2pycyc.js +1 -0
- package/.next/static/chunks/0l_~q07bhpkcx.js +7 -0
- package/.next/static/chunks/1175-dp18a1e_.js +1 -0
- package/.next/static/chunks/1552j0415oeal.js +4 -0
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/.next/types/routes.d.ts +3 -1
- package/.next/types/validator.ts +18 -0
- package/app/api/hub/server/[hostname]/agents/route.ts +62 -0
- package/app/api/hub/server/[hostname]/health/route.ts +58 -0
- package/app/api/hub/servers/route.ts +88 -1
- package/app/components/CommandPalette.tsx +41 -1
- package/app/components/HelpOverlay.tsx +55 -0
- package/app/components/ServersDrawer.tsx +181 -5
- package/app/components/TopoGraph.tsx +5514 -336
- package/app/globals.css +93 -6
- package/app/lib/vendorIdentity.ts +14 -3
- package/docs/rfc/v0.10.0-hero3-dashboard-upgrade.md +230 -0
- package/package.json +2 -2
- package/public/sleep2agi-logo.svg +11 -0
- package/public/vendors/claude.svg +11 -0
- package/public/vendors/minimax.svg +11 -0
- package/public/vendors/openai.svg +12 -0
- package/screenshots/r293-p0/after-full.png +0 -0
- package/screenshots/r293-p0/after-topo.png +0 -0
- package/screenshots/r293-p0/baseline-full.png +0 -0
- package/screenshots/r293-p0/baseline-topo.png +0 -0
- package/screenshots/r293-p0/logo-integrated-full.png +0 -0
- package/screenshots/r293-p0/logo-integrated-topo.png +0 -0
- package/screenshots/r293-p0/servers-drawer-v0.10.0-full.png +0 -0
- package/screenshots/r293-p0/servers-drawer-v0.10.0.png +0 -0
- package/screenshots/r293-p0/wave1-after-full.png +0 -0
- package/screenshots/r293-p0/wave1-after-topo.png +0 -0
- package/scripts/p0-servers-drawer-screenshot.mjs +99 -0
- package/scripts/p0-topo-screenshot.mjs +82 -0
- package/scripts/topo-active-links-chip-nav-test.mjs +117 -0
- package/scripts/topo-active-links-chip-self-hover-test.mjs +171 -0
- package/scripts/topo-active-links-empty-recede-test.mjs +125 -0
- package/scripts/topo-active-links-freshness-test.mjs +113 -0
- package/scripts/topo-active-links-tooltip-test.mjs +79 -0
- package/scripts/topo-active-pulse-ease-test.mjs +120 -0
- package/scripts/topo-brand-logo-transition-test.mjs +94 -0
- package/scripts/topo-brand-watermark-test.mjs +97 -0
- package/scripts/topo-canvas-aria-test.mjs +108 -0
- package/scripts/topo-canvas-brand-fade-test.mjs +87 -0
- package/scripts/topo-chat-target-breath-test.mjs +84 -0
- package/scripts/topo-chat-target-ring-ease-test.mjs +82 -0
- package/scripts/topo-chat-target-ring-fade-test.mjs +124 -0
- package/scripts/topo-chip-empty-recede-test.mjs +116 -0
- package/scripts/topo-chip-focus-test.mjs +95 -0
- package/scripts/topo-chip-row-gap-test.mjs +90 -0
- package/scripts/topo-chip-row-semantic-gap-test.mjs +97 -0
- package/scripts/topo-chip-row-tabular-test.mjs +111 -0
- package/scripts/topo-chip-unit-demotion-test.mjs +93 -0
- package/scripts/topo-chiprow-data-weight-test.mjs +94 -0
- package/scripts/topo-chrome-a11y-test.mjs +107 -0
- package/scripts/topo-chrome-active-weight-test.mjs +104 -0
- package/scripts/topo-chrome-focus-ring-unify-test.mjs +100 -0
- package/scripts/topo-chrome-gap-test.mjs +73 -0
- package/scripts/topo-chrome-icon-stroke-test.mjs +95 -0
- package/scripts/topo-chrome-inactive-color-test.mjs +90 -0
- package/scripts/topo-chrome-pop-extension-test.mjs +98 -0
- package/scripts/topo-chrome-pop-nodesize-test.mjs +88 -0
- package/scripts/topo-chrome-press-state-test.mjs +135 -0
- package/scripts/topo-chrome-strip-semantic-gap-test.mjs +99 -0
- package/scripts/topo-chrome-strip-theme-ease-test.mjs +99 -0
- package/scripts/topo-chrome-zoom-level-pop-test.mjs +122 -0
- package/scripts/topo-chrome-zoom-pop-test.mjs +130 -0
- package/scripts/topo-chrome-zoom-readout-weight-test.mjs +81 -0
- package/scripts/topo-click-ripple-ease-test.mjs +94 -0
- package/scripts/topo-cmdk-edge-clear-test.mjs +109 -0
- package/scripts/topo-cmdk-vendor-pin-test.mjs +103 -0
- package/scripts/topo-corner-radius-unify-test.mjs +103 -0
- package/scripts/topo-dense-alias-fill-transition-test.mjs +133 -0
- package/scripts/topo-discrete-zoom-smooth-test.mjs +125 -0
- package/scripts/topo-edge-badge-click-pin-test.mjs +94 -0
- package/scripts/topo-edge-badge-crossfade-test.mjs +138 -0
- package/scripts/topo-edge-badge-hot-test.mjs +115 -0
- package/scripts/topo-edge-badge-hover-test.mjs +85 -0
- package/scripts/topo-edge-badge-lift-test.mjs +150 -0
- package/scripts/topo-edge-badge-ripple-test.mjs +111 -0
- package/scripts/topo-edge-badge-stroke-transition-test.mjs +118 -0
- package/scripts/topo-edge-badge-text-pin-test.mjs +121 -0
- package/scripts/topo-edge-badge-theme-ease-test.mjs +89 -0
- package/scripts/topo-edge-count-badge-test.mjs +86 -0
- package/scripts/topo-edge-fadein-test.mjs +106 -0
- package/scripts/topo-edge-pill-hot-test.mjs +129 -0
- package/scripts/topo-edge-pill-test.mjs +105 -0
- package/scripts/topo-edge-ping-pulse-retired-test.mjs +119 -0
- package/scripts/topo-edge-stroke-ease-test.mjs +100 -0
- package/scripts/topo-edge-stroke-transition-test.mjs +132 -0
- package/scripts/topo-empty-hint-tracking-test.mjs +84 -0
- package/scripts/topo-empty-state-breath-test.mjs +150 -0
- package/scripts/topo-empty-state-crossfade-test.mjs +130 -0
- package/scripts/topo-empty-state-recenter-test.mjs +107 -0
- package/scripts/topo-empty-subhint-tracking-test.mjs +87 -0
- package/scripts/topo-endpoint-ring-fade-test.mjs +132 -0
- package/scripts/topo-endpoint-ring-test.mjs +93 -0
- package/scripts/topo-endpoint-ring-thicken-test.mjs +112 -0
- package/scripts/topo-filter-pill-aliases-test.mjs +121 -0
- package/scripts/topo-filter-pill-count-tabular-test.mjs +135 -0
- package/scripts/topo-freshness-chip-transition-test.mjs +80 -0
- package/scripts/topo-freshness-chip-weight-test.mjs +90 -0
- package/scripts/topo-freshness-conditional-render-test.mjs +92 -0
- package/scripts/topo-fullscreen-active-test.mjs +115 -0
- package/scripts/topo-group-label-a11y-test.mjs +113 -0
- package/scripts/topo-group-label-count-inherit-test.mjs +99 -0
- package/scripts/topo-group-label-pin-spacing-test.mjs +136 -0
- package/scripts/topo-group-label-tint-test.mjs +99 -0
- package/scripts/topo-group-label-title-test.mjs +83 -0
- package/scripts/topo-group-pip-fadein-test.mjs +104 -0
- package/scripts/topo-group-pip-singletier-drop-test.mjs +131 -0
- package/scripts/topo-group-pip-tabular-test.mjs +95 -0
- package/scripts/topo-groupbox-fadein-test.mjs +95 -0
- package/scripts/topo-groupbox-hover-lift-test.mjs +127 -0
- package/scripts/topo-groupbox-hubcore-ease-test.mjs +106 -0
- package/scripts/topo-groupbox-march-busyness-test.mjs +155 -0
- package/scripts/topo-header-gap-test.mjs +94 -0
- package/scripts/topo-help-overlay-r109-test.mjs +67 -0
- package/scripts/topo-help-overlay-r118-test.mjs +69 -0
- package/scripts/topo-help-overlay-r123-test.mjs +69 -0
- package/scripts/topo-help-overlay-r134-test.mjs +74 -0
- package/scripts/topo-help-overlay-r137-test.mjs +81 -0
- package/scripts/topo-help-overlay-r141-test.mjs +86 -0
- package/scripts/topo-help-overlay-r153-test.mjs +85 -0
- package/scripts/topo-hot-tail-crossfade-test.mjs +117 -0
- package/scripts/topo-html-overlay-padding-unify-test.mjs +120 -0
- package/scripts/topo-hub-a11y-test.mjs +112 -0
- package/scripts/topo-hub-center-crossfade-test.mjs +124 -0
- package/scripts/topo-hub-digit-scale-test.mjs +129 -0
- package/scripts/topo-hub-fadein-test.mjs +88 -0
- package/scripts/topo-hub-hover-lift-test.mjs +101 -0
- package/scripts/topo-hub-hover-ring-test.mjs +82 -0
- package/scripts/topo-hub-pip-theme-ease-test.mjs +116 -0
- package/scripts/topo-hub-spoke-ease-test.mjs +120 -0
- package/scripts/topo-hub-working-count-test.mjs +99 -0
- package/scripts/topo-inner-radar-ring-retired-test.mjs +89 -0
- package/scripts/topo-kicker-color-test.mjs +93 -0
- package/scripts/topo-kicker-tracking-test.mjs +101 -0
- package/scripts/topo-kicker-weight-test.mjs +98 -0
- package/scripts/topo-label-card-theme-ease-test.mjs +85 -0
- package/scripts/topo-layout-hover-text-preview-test.mjs +101 -0
- package/scripts/topo-layout-switch-crossfade-test.mjs +126 -0
- package/scripts/topo-layout-toggle-border-unify-test.mjs +104 -0
- package/scripts/topo-layout-toggle-test.mjs +131 -0
- package/scripts/topo-layout-trailer-mr-test.mjs +82 -0
- package/scripts/topo-legend-compress-test.mjs +107 -0
- package/scripts/topo-legend-count-empty-test.mjs +126 -0
- package/scripts/topo-legend-count-tabular-parity-test.mjs +90 -0
- package/scripts/topo-legend-count-tabular-test.mjs +87 -0
- package/scripts/topo-legend-count-test.mjs +88 -0
- package/scripts/topo-legend-count-tier-fill-test.mjs +121 -0
- package/scripts/topo-legend-count-weight-test.mjs +86 -0
- package/scripts/topo-legend-header-symmetric-inset-test.mjs +112 -0
- package/scripts/topo-legend-hitbox-swatch-center-test.mjs +93 -0
- package/scripts/topo-legend-offline-delimiter-test.mjs +84 -0
- package/scripts/topo-legend-offline-label-simplify-test.mjs +86 -0
- package/scripts/topo-legend-panel-count-weight-test.mjs +89 -0
- package/scripts/topo-legend-panel-header-test.mjs +83 -0
- package/scripts/topo-legend-pin-ring-fade-test.mjs +112 -0
- package/scripts/topo-legend-row-hover-lift-test.mjs +109 -0
- package/scripts/topo-legend-row-labels-simplify-test.mjs +80 -0
- package/scripts/topo-legend-row-pin-spacing-test.mjs +124 -0
- package/scripts/topo-legend-row-tint-test.mjs +95 -0
- package/scripts/topo-legend-row-title-test.mjs +102 -0
- package/scripts/topo-legend-swatch-grow-test.mjs +116 -0
- package/scripts/topo-legend-swatch-radius-test.mjs +78 -0
- package/scripts/topo-minimap-a11y-test.mjs +101 -0
- package/scripts/topo-minimap-dot-transition-test.mjs +113 -0
- package/scripts/topo-minimap-radius-test.mjs +88 -0
- package/scripts/topo-minimap-rect-stroke-test.mjs +94 -0
- package/scripts/topo-minimap-viewport-smooth-test.mjs +124 -0
- package/scripts/topo-monogram-font-test.mjs +112 -0
- package/scripts/topo-monogram-stroke-test.mjs +101 -0
- package/scripts/topo-more-flows-letterspacing-test.mjs +111 -0
- package/scripts/topo-node-a11y-test.mjs +107 -0
- package/scripts/topo-node-alias-chat-pin-test.mjs +95 -0
- package/scripts/topo-node-label-card-elevation-test.mjs +157 -0
- package/scripts/topo-node-label-card-stroke-test.mjs +135 -0
- package/scripts/topo-node-stroke-transition-test.mjs +103 -0
- package/scripts/topo-node-text-fill-transition-test.mjs +119 -0
- package/scripts/topo-node-title-peers-test.mjs +107 -0
- package/scripts/topo-node-title-test.mjs +101 -0
- package/scripts/topo-nodesize-active-hover-test.mjs +109 -0
- package/scripts/topo-nodesize-crossfade-test.mjs +141 -0
- package/scripts/topo-online-chip-nav-test.mjs +143 -0
- package/scripts/topo-orbit-particles-retired-test.mjs +91 -0
- package/scripts/topo-panel-count-unit-test.mjs +112 -0
- package/scripts/topo-panel-fadein-test.mjs +102 -0
- package/scripts/topo-panel-hot-tail-fade-test.mjs +132 -0
- package/scripts/topo-panel-hover-elevation-test.mjs +118 -0
- package/scripts/topo-panel-rx-test.mjs +90 -0
- package/scripts/topo-panel-theme-ease-test.mjs +85 -0
- package/scripts/topo-panel-title-fill-ease-test.mjs +91 -0
- package/scripts/topo-panel-title-tracking-test.mjs +78 -0
- package/scripts/topo-particle-stagger-test.mjs +82 -0
- package/scripts/topo-pin-intersection-color-ease-test.mjs +111 -0
- package/scripts/topo-pin-intersection-empty-test.mjs +129 -0
- package/scripts/topo-pin-intersection-tabular-test.mjs +96 -0
- package/scripts/topo-pin-intersection-test.mjs +121 -0
- package/scripts/topo-pin-intersection-warning-crossfade-test.mjs +107 -0
- package/scripts/topo-pin-mirror-transition-test.mjs +101 -0
- package/scripts/topo-prefix-opacity-test.mjs +107 -0
- package/scripts/topo-prepublish-guard-test.mjs +95 -0
- package/scripts/topo-pressure-seg-self-hover-test.mjs +126 -0
- package/scripts/topo-pressure-seg-tooltip-test.mjs +98 -0
- package/scripts/topo-pressure-seg-transition-test.mjs +107 -0
- package/scripts/topo-pulse-dot-crossfade-test.mjs +122 -0
- package/scripts/topo-pulse-dot-retired-test.mjs +86 -0
- package/scripts/topo-recent-panel-count-freshness-test.mjs +109 -0
- package/scripts/topo-recent-panel-count-test.mjs +72 -0
- package/scripts/topo-recent-panel-count-weight-test.mjs +91 -0
- package/scripts/topo-recent-panel-footer-breath-test.mjs +142 -0
- package/scripts/topo-recent-panel-hot-header-test.mjs +132 -0
- package/scripts/topo-recent-panel-hot-tabular-test.mjs +113 -0
- package/scripts/topo-recent-panel-more-crossfade-test.mjs +124 -0
- package/scripts/topo-recent-panel-more-cyan-hover-test.mjs +151 -0
- package/scripts/topo-recent-panel-more-nav-test.mjs +127 -0
- package/scripts/topo-recent-panel-more-test.mjs +106 -0
- package/scripts/topo-recent-panel-readability-bump-test.mjs +151 -0
- package/scripts/topo-recent-row-count-fill-test.mjs +129 -0
- package/scripts/topo-recent-row-count-weight-test.mjs +112 -0
- package/scripts/topo-recent-row-fadein-test.mjs +107 -0
- package/scripts/topo-recent-row-freshness-test.mjs +102 -0
- package/scripts/topo-recent-row-hot-test.mjs +110 -0
- package/scripts/topo-recent-row-hover-lift-test.mjs +128 -0
- package/scripts/topo-recent-row-pin-spacing-test.mjs +137 -0
- package/scripts/topo-recent-row-pin-test.mjs +126 -0
- package/scripts/topo-recent-row-tint-test.mjs +91 -0
- package/scripts/topo-recent-row-title-test.mjs +112 -0
- package/scripts/topo-recent-row-ts-alpha-test.mjs +100 -0
- package/scripts/topo-recent-row-ts-tabular-test.mjs +108 -0
- package/scripts/topo-recent-row-ts-test.mjs +95 -0
- package/scripts/topo-recent-row-typography-test.mjs +119 -0
- package/scripts/topo-recent-signal-empty-hint-test.mjs +73 -0
- package/scripts/topo-reset-spin-test.mjs +120 -0
- package/scripts/topo-runtime-badge-grow-test.mjs +114 -0
- package/scripts/topo-spokes-retired-test.mjs +93 -0
- package/scripts/topo-starfield-density-test.mjs +89 -0
- package/scripts/topo-status-chip-tooltip-test.mjs +95 -0
- package/scripts/topo-statusbar-chips-self-hover-test.mjs +177 -0
- package/scripts/topo-svg-focus-test.mjs +112 -0
- package/scripts/topo-sweep-busyness-test.mjs +94 -0
- package/scripts/topo-tabular-nums-sweep-test.mjs +93 -0
- package/scripts/topo-tier-guide-dashes-test.mjs +86 -0
- package/scripts/topo-tier-ring-fadein-test.mjs +107 -0
- package/scripts/topo-tier-ring-tint-test.mjs +110 -0
- package/scripts/topo-title-block-tight-leading-test.mjs +95 -0
- package/scripts/topo-title-tracking-test.mjs +98 -0
- package/scripts/topo-titleblock-gap-test.mjs +86 -0
- package/scripts/topo-titleblock-margin-test.mjs +83 -0
- package/scripts/topo-toggle-inactive-cyan-hover-test.mjs +110 -0
- package/scripts/topo-top-rail-theme-ease-test.mjs +89 -0
- package/scripts/topo-vendor-chip-self-hover-test.mjs +131 -0
- package/scripts/topo-vendor-chip-tabular-test.mjs +92 -0
- package/scripts/topo-vendor-chip-threshold-test.mjs +78 -0
- package/scripts/topo-vendor-chip-weight-test.mjs +87 -0
- package/scripts/topo-vendor-count-suffix-test.mjs +95 -0
- package/scripts/topo-vendor-letter-tooltip-test.mjs +92 -0
- package/scripts/topo-view-smooth-test.mjs +131 -0
- package/scripts/topo-watermark-tracking-test.mjs +99 -0
- package/scripts/topo-working-chip-pin-test.mjs +154 -0
- package/scripts/topo-working-halo-breath-retired-test.mjs +99 -0
- package/scripts/topo-wrapper-flow-arrow-theme-test.mjs +97 -0
- package/scripts/topo-wrapper-radius-test.mjs +73 -0
- package/scripts/topo-wrapper-shadow-theme-ease-test.mjs +89 -0
- package/.next/static/chunks/03w4rgx_vt8ec.js +0 -1
- package/.next/static/chunks/072so8rfx2tt-.css +0 -2
- package/.next/static/chunks/0o.k-.0kl4bz5.js +0 -2
- package/.next/static/chunks/121d8yvfl8l_..js +0 -7
- package/.next/static/chunks/148o1e4ijgwp1.js +0 -1
- package/scripts/topo-arrival-ping-test.mjs +0 -87
- package/scripts/topo-dispatch-pulse-test.mjs +0 -83
- /package/.next/static/{Dhky2WTh8mvJhw56taHVs → 424zX3i80AJ_wHF_otKuP}/_buildManifest.js +0 -0
- /package/.next/static/{Dhky2WTh8mvJhw56taHVs → 424zX3i80AJ_wHF_otKuP}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{Dhky2WTh8mvJhw56taHVs → 424zX3i80AJ_wHF_otKuP}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/* Round 255 verification: chrome-strip semantic gap.
|
|
2
|
+
*
|
|
3
|
+
* Pre-R255 the bottom-right chrome strip had all 4 groups at uniform
|
|
4
|
+
* gap-1.5 (6px):
|
|
5
|
+
* [ S | M | L ] 6px [ - | % | + ] 6px [ reset ] 6px [ fullscreen ]
|
|
6
|
+
*
|
|
7
|
+
* R255 doubles the gap before the first view-control group via ml-1.5
|
|
8
|
+
* on the zoom container — stacks on top of parent's gap-1.5 (6+6=12px):
|
|
9
|
+
* [ S | M | L ] 12px [ - | % | + ] 6px [ reset ] 6px [ fullscreen ]
|
|
10
|
+
* ↑
|
|
11
|
+
* semantic boundary: fleet | view
|
|
12
|
+
*
|
|
13
|
+
* Test scope:
|
|
14
|
+
* 1. Both data attrs resolve to elements.
|
|
15
|
+
* 2. Gap between fleet group's right edge and view group's left edge
|
|
16
|
+
* is ≥ 11px (allowing 1px sub-pixel rounding from 12px target).
|
|
17
|
+
* 3. Gap between adjacent view-control elements (zoom→reset, reset→
|
|
18
|
+
* fullscreen) stays at ~6px (the parent gap-1.5) — confirms the
|
|
19
|
+
* ml-1.5 didn't accidentally widen ALL gaps.
|
|
20
|
+
* 4. Vertical alignment preserved — both groups still share the same
|
|
21
|
+
* Y center (chrome strip didn't get visually broken).
|
|
22
|
+
*/
|
|
23
|
+
import { chromium } from 'playwright';
|
|
24
|
+
import { readFileSync } from 'node:fs';
|
|
25
|
+
|
|
26
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
27
|
+
const browser = await chromium.launch({ headless: true });
|
|
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) => ({
|
|
39
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
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'), mk('beta'), mk('gamma'), mk('delta'),
|
|
45
|
+
] } });
|
|
46
|
+
});
|
|
47
|
+
await ctx.route('**/api/hub/messages*', (r2) => r2.fulfill({ json: { messages: [] } }));
|
|
48
|
+
await ctx.route('**/api/hub/tasks*', (r2) => r2.fulfill({ json: { tasks: [] } }));
|
|
49
|
+
|
|
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-topo-chrome-fleet-group-trailer]', { timeout: 10000 });
|
|
54
|
+
await page.waitForSelector('[data-topo-chrome-view-group-leader]', { timeout: 10000 });
|
|
55
|
+
await page.waitForTimeout(300);
|
|
56
|
+
|
|
57
|
+
const probe = await page.evaluate(() => {
|
|
58
|
+
const fleet = document.querySelector('[data-topo-chrome-fleet-group-trailer]');
|
|
59
|
+
const view = document.querySelector('[data-topo-chrome-view-group-leader]');
|
|
60
|
+
const reset = document.querySelector('[data-topo-chrome-reset]');
|
|
61
|
+
const fullscreen = document.querySelector('[data-topo-chrome-fullscreen]');
|
|
62
|
+
const r = (el) => el ? el.getBoundingClientRect() : null;
|
|
63
|
+
return {
|
|
64
|
+
fleet: r(fleet),
|
|
65
|
+
view: r(view),
|
|
66
|
+
reset: r(reset),
|
|
67
|
+
fullscreen: r(fullscreen),
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
await browser.close();
|
|
71
|
+
|
|
72
|
+
const gapFleetToView = probe.view && probe.fleet ? probe.view.left - probe.fleet.right : null;
|
|
73
|
+
const gapViewToReset = probe.reset && probe.view ? probe.reset.left - probe.view.right : null;
|
|
74
|
+
const gapResetToFull = probe.fullscreen && probe.reset ? probe.fullscreen.left - probe.reset.right : null;
|
|
75
|
+
const yAlignedFleetView = probe.fleet && probe.view ? Math.abs((probe.fleet.top + probe.fleet.height / 2)
|
|
76
|
+
- (probe.view.top + probe.view.height / 2)) : null;
|
|
77
|
+
|
|
78
|
+
const results = {
|
|
79
|
+
fleet_present: probe.fleet !== null,
|
|
80
|
+
view_present: probe.view !== null,
|
|
81
|
+
reset_present: probe.reset !== null,
|
|
82
|
+
fullscreen_present: probe.fullscreen !== null,
|
|
83
|
+
|
|
84
|
+
// Doubled gap fleet→view: target 12px, accept ≥11 for sub-pixel
|
|
85
|
+
fleet_to_view_gap_doubled: gapFleetToView != null && gapFleetToView >= 11,
|
|
86
|
+
|
|
87
|
+
// Adjacent view-control gaps stay at ~6px (the parent gap-1.5).
|
|
88
|
+
// Accept 5-9px window for sub-pixel + Tailwind v4 token drift.
|
|
89
|
+
view_to_reset_gap_unchanged: gapViewToReset != null && gapViewToReset >= 5 && gapViewToReset <= 9,
|
|
90
|
+
reset_to_full_gap_unchanged: gapResetToFull != null && gapResetToFull >= 5 && gapResetToFull <= 9,
|
|
91
|
+
|
|
92
|
+
// Vertical center alignment preserved (≤1px slop).
|
|
93
|
+
y_aligned_fleet_view: yAlignedFleetView != null && yAlignedFleetView <= 1,
|
|
94
|
+
};
|
|
95
|
+
const ok = Object.values(results).every(Boolean);
|
|
96
|
+
console.log(`${ok ? '✅' : '❌'} chrome strip semantic gap:`, JSON.stringify(results),
|
|
97
|
+
'\n gaps: fleet→view =', gapFleetToView, 'view→reset =', gapViewToReset, 'reset→full =', gapResetToFull,
|
|
98
|
+
'\n yAlignedΔ:', yAlignedFleetView);
|
|
99
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/* Round 264 verification: chrome strip wrappers + zoom level readout
|
|
2
|
+
* pick up theme-toggle transitions — close R263's R254-holdover sweep.
|
|
3
|
+
*
|
|
4
|
+
* Pre-R264 the chrome strip's STRUCTURAL pieces had theme-driven
|
|
5
|
+
* inline styles but no transition coverage:
|
|
6
|
+
*
|
|
7
|
+
* nodeSize wrapper (line ~6603): bg + borderColor inline, no transition
|
|
8
|
+
* zoom wrapper (line ~6666): bg + borderColor inline, no transition
|
|
9
|
+
* zoom level readout (line ~6711): color + border-x inline, no transition
|
|
10
|
+
*
|
|
11
|
+
* Reset + fullscreen buttons already had `transition-colors` className
|
|
12
|
+
* covering their bg/border/color theme swaps. Inner S/M/L and -/+
|
|
13
|
+
* buttons same. So inner buttons eased smoothly through theme but the
|
|
14
|
+
* surrounding wrappers + readout SNAPPED — a layered hard-cut where
|
|
15
|
+
* the bordered container hard-flipped under the eased buttons inside.
|
|
16
|
+
*
|
|
17
|
+
* R264 adds 200ms theme transitions to all three structural pieces.
|
|
18
|
+
*
|
|
19
|
+
* Test scope:
|
|
20
|
+
* 1. nodeSize wrapper transition contains background-color + border-color.
|
|
21
|
+
* 2. zoom wrapper (data-topo-chrome-zoom-wrapper) same.
|
|
22
|
+
* 3. zoom level readout transition contains color + border-color.
|
|
23
|
+
* 4. R263 wrapper invariant: top-level wrapper transition still has
|
|
24
|
+
* box-shadow (regression).
|
|
25
|
+
*/
|
|
26
|
+
import { chromium } from 'playwright';
|
|
27
|
+
import { readFileSync } from 'node:fs';
|
|
28
|
+
|
|
29
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
30
|
+
const browser = await chromium.launch({ headless: true });
|
|
31
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
32
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
33
|
+
await ctx.addInitScript(() => {
|
|
34
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
35
|
+
});
|
|
36
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
37
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
38
|
+
const r = await route.fetch();
|
|
39
|
+
const b = await r.json();
|
|
40
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
41
|
+
const mk = (alias) => ({
|
|
42
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
43
|
+
network_id: nid, project_dir: null,
|
|
44
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
45
|
+
});
|
|
46
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
47
|
+
mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
|
|
48
|
+
] } });
|
|
49
|
+
});
|
|
50
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
51
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
52
|
+
|
|
53
|
+
const page = await ctx.newPage();
|
|
54
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
55
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
56
|
+
await page.waitForSelector('[data-topo-chrome-fleet-group-trailer]', { timeout: 10000 });
|
|
57
|
+
await page.waitForSelector('[data-topo-chrome-zoom-wrapper]', { timeout: 10000 });
|
|
58
|
+
await page.waitForSelector('[data-topo-chrome-zoom-level]', { timeout: 10000 });
|
|
59
|
+
await page.waitForSelector('[data-topo-wrapper]', { timeout: 10000 });
|
|
60
|
+
await page.waitForTimeout(300);
|
|
61
|
+
|
|
62
|
+
const probe = await page.evaluate(() => {
|
|
63
|
+
const nodeSize = document.querySelector('[data-topo-chrome-fleet-group-trailer]');
|
|
64
|
+
const zoom = document.querySelector('[data-topo-chrome-zoom-wrapper]');
|
|
65
|
+
const zoomLevel = document.querySelector('[data-topo-chrome-zoom-level]');
|
|
66
|
+
const wrapper = document.querySelector('[data-topo-wrapper]');
|
|
67
|
+
return {
|
|
68
|
+
nodeSizeTransition: nodeSize ? nodeSize.style.transition : null,
|
|
69
|
+
zoomTransition: zoom ? zoom.style.transition : null,
|
|
70
|
+
zoomLevelTransition: zoomLevel ? zoomLevel.style.transition : null,
|
|
71
|
+
wrapperTransition: wrapper ? wrapper.style.transition : null,
|
|
72
|
+
};
|
|
73
|
+
});
|
|
74
|
+
await browser.close();
|
|
75
|
+
|
|
76
|
+
const has = (s, prop) => new RegExp(`${prop}\\s+(?:200ms|0\\.2s)`).test(s || '');
|
|
77
|
+
|
|
78
|
+
const results = {
|
|
79
|
+
nodesize_present: probe.nodeSizeTransition !== null,
|
|
80
|
+
nodesize_has_bg_color_200: has(probe.nodeSizeTransition, 'background-color'),
|
|
81
|
+
nodesize_has_border_color_200: has(probe.nodeSizeTransition, 'border-color'),
|
|
82
|
+
|
|
83
|
+
zoom_present: probe.zoomTransition !== null,
|
|
84
|
+
zoom_has_bg_color_200: has(probe.zoomTransition, 'background-color'),
|
|
85
|
+
zoom_has_border_color_200: has(probe.zoomTransition, 'border-color'),
|
|
86
|
+
|
|
87
|
+
zoom_level_present: probe.zoomLevelTransition !== null,
|
|
88
|
+
zoom_level_has_color_200: has(probe.zoomLevelTransition, '(?<!background-|border-)color'),
|
|
89
|
+
zoom_level_has_border_color_200: has(probe.zoomLevelTransition, 'border-color'),
|
|
90
|
+
|
|
91
|
+
r263_wrapper_box_shadow_200: has(probe.wrapperTransition, 'box-shadow'),
|
|
92
|
+
};
|
|
93
|
+
const ok = Object.values(results).every(Boolean);
|
|
94
|
+
console.log(`${ok ? '✅' : '❌'} chrome strip theme ease:`, JSON.stringify(results),
|
|
95
|
+
'\n nodeSize: ', probe.nodeSizeTransition,
|
|
96
|
+
'\n zoom: ', probe.zoomTransition,
|
|
97
|
+
'\n zoomLevel: ', probe.zoomLevelTransition,
|
|
98
|
+
'\n wrapper: ', probe.wrapperTransition);
|
|
99
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/* Round 192 verification: the zoom-level readout span (the "%" text
|
|
2
|
+
* between + and − buttons) participates in the R186 click-feel pop
|
|
3
|
+
* alongside the icons.
|
|
4
|
+
*
|
|
5
|
+
* Pre-R192 only the + / − icons popped on click; the readout text
|
|
6
|
+
* itself snapped from one value to another with no acknowledgement.
|
|
7
|
+
* R192 splices the existing .anet-chrome-pop class onto the readout
|
|
8
|
+
* span while chromePopping is 'zoom-in' or 'zoom-out' (set by the
|
|
9
|
+
* shared popChrome flag the icons already use).
|
|
10
|
+
*
|
|
11
|
+
* Test:
|
|
12
|
+
* 1. Default state — readout has data-topo-chrome-zoom-level-popping
|
|
13
|
+
* = 'false', no 'anet-chrome-pop' in className.
|
|
14
|
+
* 2. Click zoom-in: capture the readout className within ~120ms.
|
|
15
|
+
* Should have 'anet-chrome-pop' + data-popping='true'.
|
|
16
|
+
* 3. Wait past the 240ms class clear: readout className back to
|
|
17
|
+
* base, data-popping='false'.
|
|
18
|
+
* 4. Click zoom-out: same pop activation flips on then back off.
|
|
19
|
+
* 5. Display rule: span must NOT be 'inline' so the transform:
|
|
20
|
+
* scale() from the keyframe actually renders. (Inline style
|
|
21
|
+
* sets display:'inline-block' for that reason — but the parent
|
|
22
|
+
* <div class="flex"> blockifies it to 'block' in computed
|
|
23
|
+
* style, which is fine for transforms either way.)
|
|
24
|
+
*/
|
|
25
|
+
import { chromium } from 'playwright';
|
|
26
|
+
import { readFileSync } from 'node:fs';
|
|
27
|
+
|
|
28
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
29
|
+
const browser = await chromium.launch({ headless: true });
|
|
30
|
+
|
|
31
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
32
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
33
|
+
await ctx.addInitScript(() => {
|
|
34
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
38
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
39
|
+
const r = await route.fetch();
|
|
40
|
+
const b = await r.json();
|
|
41
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
42
|
+
const mk = (alias) => ({
|
|
43
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
44
|
+
network_id: nid, project_dir: null,
|
|
45
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
46
|
+
});
|
|
47
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
48
|
+
mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
|
|
49
|
+
] } });
|
|
50
|
+
});
|
|
51
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
52
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
53
|
+
|
|
54
|
+
const page = await ctx.newPage();
|
|
55
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
56
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
57
|
+
await page.waitForSelector('[data-topo-chrome-zoom-level]', { timeout: 10000 });
|
|
58
|
+
await page.waitForTimeout(300);
|
|
59
|
+
|
|
60
|
+
const readSpan = async () => page.evaluate(() => {
|
|
61
|
+
const el = document.querySelector('[data-topo-chrome-zoom-level]');
|
|
62
|
+
if (!el) return null;
|
|
63
|
+
return {
|
|
64
|
+
className: el.getAttribute('class') || '',
|
|
65
|
+
popping: el.getAttribute('data-topo-chrome-zoom-level-popping'),
|
|
66
|
+
display: getComputedStyle(el).display,
|
|
67
|
+
text: el.textContent?.trim() || '',
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const idle = await readSpan();
|
|
72
|
+
|
|
73
|
+
// Click zoom-in and snapshot quickly
|
|
74
|
+
await page.click('[data-topo-chrome-zoom-in]');
|
|
75
|
+
await page.waitForTimeout(80); // well inside the 240ms class window
|
|
76
|
+
const popped = await readSpan();
|
|
77
|
+
|
|
78
|
+
// Wait past the popChrome clear (~260ms total)
|
|
79
|
+
await page.waitForTimeout(280);
|
|
80
|
+
const settled = await readSpan();
|
|
81
|
+
|
|
82
|
+
// Click zoom-out and snapshot quickly
|
|
83
|
+
await page.click('[data-topo-chrome-zoom-out]');
|
|
84
|
+
await page.waitForTimeout(80);
|
|
85
|
+
const poppedOut = await readSpan();
|
|
86
|
+
|
|
87
|
+
await page.waitForTimeout(280);
|
|
88
|
+
const settled2 = await readSpan();
|
|
89
|
+
|
|
90
|
+
await browser.close();
|
|
91
|
+
|
|
92
|
+
const has = (s, c) => (s?.className || '').split(/\s+/).includes(c);
|
|
93
|
+
|
|
94
|
+
const results = {
|
|
95
|
+
idle_no_pop_class: !has(idle, 'anet-chrome-pop'),
|
|
96
|
+
idle_popping_false: idle?.popping === 'false',
|
|
97
|
+
idle_has_base_classes: /tabular-nums/.test(idle?.className || '') && /border-x/.test(idle?.className || ''),
|
|
98
|
+
display_not_inline: idle?.display !== 'inline',
|
|
99
|
+
|
|
100
|
+
popped_has_pop_class: has(popped, 'anet-chrome-pop'),
|
|
101
|
+
popped_popping_true: popped?.popping === 'true',
|
|
102
|
+
popped_keeps_base: /tabular-nums/.test(popped?.className || ''),
|
|
103
|
+
|
|
104
|
+
settled_no_pop_class: !has(settled, 'anet-chrome-pop'),
|
|
105
|
+
settled_popping_false: settled?.popping === 'false',
|
|
106
|
+
|
|
107
|
+
poppedOut_has_pop_class: has(poppedOut, 'anet-chrome-pop'),
|
|
108
|
+
poppedOut_popping_true: poppedOut?.popping === 'true',
|
|
109
|
+
|
|
110
|
+
settled2_no_pop_class: !has(settled2, 'anet-chrome-pop'),
|
|
111
|
+
settled2_popping_false: settled2?.popping === 'false',
|
|
112
|
+
|
|
113
|
+
text_shows_percent: /^\d+%$/.test(idle?.text || ''),
|
|
114
|
+
};
|
|
115
|
+
const ok = Object.values(results).every(Boolean);
|
|
116
|
+
console.log(`${ok ? '✅' : '❌'} chrome zoom-level pop:`, JSON.stringify(results),
|
|
117
|
+
`\n idle:`, idle,
|
|
118
|
+
`\n popped:`, popped,
|
|
119
|
+
`\n settled:`, settled,
|
|
120
|
+
`\n poppedOut:`, poppedOut,
|
|
121
|
+
`\n settled2:`, settled2);
|
|
122
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/* Round 186 verification: chrome zoom-in / zoom-out buttons get
|
|
2
|
+
* a one-shot icon "pop" on click — same click-feel idiom R184
|
|
3
|
+
* added for the reset spin, applied to the two zoom buttons.
|
|
4
|
+
*
|
|
5
|
+
* Test:
|
|
6
|
+
* 1. Idle: neither popping
|
|
7
|
+
* 2. Click chrome zoom-in → zoom-in icon gets 'anet-chrome-pop',
|
|
8
|
+
* attr 'true' within 50ms
|
|
9
|
+
* 3. Wait 280ms → class + attr cleared
|
|
10
|
+
* 4. Click chrome zoom-out → zoom-out icon gets the class, attr
|
|
11
|
+
* 'true'; zoom-in stays cleared
|
|
12
|
+
* 5. CSS keyframe 'anet-chrome-pop' exists in stylesheet and
|
|
13
|
+
* includes scale(1.18)
|
|
14
|
+
*/
|
|
15
|
+
import { chromium } from 'playwright';
|
|
16
|
+
import { readFileSync } from 'node:fs';
|
|
17
|
+
|
|
18
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
19
|
+
const browser = await chromium.launch({ headless: true });
|
|
20
|
+
|
|
21
|
+
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
|
+
|
|
27
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
28
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
29
|
+
const r = await route.fetch();
|
|
30
|
+
const b = await r.json();
|
|
31
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
32
|
+
const mk = (alias) => ({
|
|
33
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
34
|
+
network_id: nid, project_dir: null,
|
|
35
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
36
|
+
});
|
|
37
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta'), mk('gamma')] } });
|
|
38
|
+
});
|
|
39
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
40
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
41
|
+
|
|
42
|
+
const page = await ctx.newPage();
|
|
43
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
44
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
|
|
45
|
+
await page.waitForSelector('[data-topo-chrome-zoom-in]', { timeout: 10000 });
|
|
46
|
+
await page.waitForTimeout(400);
|
|
47
|
+
|
|
48
|
+
const probe = () => page.evaluate(() => {
|
|
49
|
+
const inBtn = document.querySelector('[data-topo-chrome-zoom-in]');
|
|
50
|
+
const outBtn = document.querySelector('[data-topo-chrome-zoom-out]');
|
|
51
|
+
const inIcon = document.querySelector('[data-topo-chrome-zoom-in-icon]');
|
|
52
|
+
const outIcon = document.querySelector('[data-topo-chrome-zoom-out-icon]');
|
|
53
|
+
return {
|
|
54
|
+
inAttr: inBtn?.getAttribute('data-topo-chrome-zoom-in-popping'),
|
|
55
|
+
outAttr: outBtn?.getAttribute('data-topo-chrome-zoom-out-popping'),
|
|
56
|
+
inClass: inIcon?.getAttribute('class') || '',
|
|
57
|
+
outClass: outIcon?.getAttribute('class') || '',
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const idle = await probe();
|
|
62
|
+
|
|
63
|
+
await page.locator('[data-topo-chrome-zoom-in]').click();
|
|
64
|
+
await page.waitForTimeout(50);
|
|
65
|
+
const duringIn = await probe();
|
|
66
|
+
|
|
67
|
+
await page.waitForTimeout(280);
|
|
68
|
+
const afterIn = await probe();
|
|
69
|
+
|
|
70
|
+
await page.locator('[data-topo-chrome-zoom-out]').click();
|
|
71
|
+
await page.waitForTimeout(50);
|
|
72
|
+
const duringOut = await probe();
|
|
73
|
+
|
|
74
|
+
await page.waitForTimeout(280);
|
|
75
|
+
const afterOut = await probe();
|
|
76
|
+
|
|
77
|
+
const cssCheck = await page.evaluate(() => {
|
|
78
|
+
for (const sheet of document.styleSheets) {
|
|
79
|
+
let rules;
|
|
80
|
+
try { rules = sheet.cssRules; } catch { continue; }
|
|
81
|
+
if (!rules) continue;
|
|
82
|
+
for (const rule of rules) {
|
|
83
|
+
if (rule.type === 7 && rule.name === 'anet-chrome-pop') {
|
|
84
|
+
return { found: true, cssText: rule.cssText };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return { found: false, cssText: '' };
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
await browser.close();
|
|
92
|
+
|
|
93
|
+
const results = {
|
|
94
|
+
idle_in_attr_false: idle.inAttr === 'false',
|
|
95
|
+
idle_out_attr_false: idle.outAttr === 'false',
|
|
96
|
+
idle_no_in_pop: !idle.inClass.includes('anet-chrome-pop'),
|
|
97
|
+
idle_no_out_pop: !idle.outClass.includes('anet-chrome-pop'),
|
|
98
|
+
|
|
99
|
+
// After zoom-in click: in popping, out static
|
|
100
|
+
in_attr_true: duringIn.inAttr === 'true',
|
|
101
|
+
in_has_pop_class: duringIn.inClass.includes('anet-chrome-pop'),
|
|
102
|
+
out_stays_false: duringIn.outAttr === 'false',
|
|
103
|
+
out_no_pop_class: !duringIn.outClass.includes('anet-chrome-pop'),
|
|
104
|
+
|
|
105
|
+
// After 280ms: in cleared
|
|
106
|
+
after_in_attr_false: afterIn.inAttr === 'false',
|
|
107
|
+
after_in_no_pop: !afterIn.inClass.includes('anet-chrome-pop'),
|
|
108
|
+
|
|
109
|
+
// After zoom-out click: out popping
|
|
110
|
+
out_attr_true: duringOut.outAttr === 'true',
|
|
111
|
+
out_has_pop_class: duringOut.outClass.includes('anet-chrome-pop'),
|
|
112
|
+
in_stays_false: duringOut.inAttr === 'false',
|
|
113
|
+
in_no_pop_after: !duringOut.inClass.includes('anet-chrome-pop'),
|
|
114
|
+
|
|
115
|
+
// After 280ms: out cleared
|
|
116
|
+
after_out_attr_false: afterOut.outAttr === 'false',
|
|
117
|
+
after_out_no_pop: !afterOut.outClass.includes('anet-chrome-pop'),
|
|
118
|
+
|
|
119
|
+
keyframe_found: cssCheck.found,
|
|
120
|
+
keyframe_has_scale_1p18: cssCheck.cssText.includes('scale(1.18)'),
|
|
121
|
+
};
|
|
122
|
+
const ok = Object.values(results).every(Boolean);
|
|
123
|
+
console.log(`${ok ? '✅' : '❌'} chrome zoom pop:`, JSON.stringify(results),
|
|
124
|
+
`\n idle:`, idle,
|
|
125
|
+
`\n duringIn:`, duringIn,
|
|
126
|
+
`\n afterIn:`, afterIn,
|
|
127
|
+
`\n duringOut:`, duringOut,
|
|
128
|
+
`\n afterOut:`, afterOut,
|
|
129
|
+
`\n keyframe found:`, cssCheck.found);
|
|
130
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/* Round 312 verification: chrome strip zoom readout '{N}%' picks
|
|
2
|
+
* up font-medium (500). Extends the R309-R311 data-digit weight
|
|
3
|
+
* rule to the chrome strip's only live data display. font-medium
|
|
4
|
+
* vs the SVG panel counts' fontWeight=600 keeps a tier between
|
|
5
|
+
* SVG-panel data (600) and HTML-chrome data (500), preventing
|
|
6
|
+
* the chrome readout from competing with the SVG panel counts.
|
|
7
|
+
*
|
|
8
|
+
* Contract:
|
|
9
|
+
* - [data-topo-chrome-zoom-level] className contains
|
|
10
|
+
* 'font-medium'. Computed font-weight resolves to 500.
|
|
11
|
+
* - tabular-nums still in className (R225 regression).
|
|
12
|
+
* - text content matches '{N}%' pattern.
|
|
13
|
+
* - R311 recent count fw=600 + R310 legend panel count fw=600
|
|
14
|
+
* + R309 row counts fw=600 + R294 pulse absent all preserved.
|
|
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) => ({
|
|
32
|
+
alias, status: 'working', 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'),
|
|
38
|
+
mk('beta', 'gpt-4o'),
|
|
39
|
+
] } });
|
|
40
|
+
});
|
|
41
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
42
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
43
|
+
|
|
44
|
+
const page = await ctx.newPage();
|
|
45
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
46
|
+
await page.waitForSelector('[data-topo-chrome-zoom-level]', { timeout: 15000 });
|
|
47
|
+
await page.waitForTimeout(300);
|
|
48
|
+
|
|
49
|
+
const probe = await page.evaluate(() => {
|
|
50
|
+
const sel = (s) => document.querySelector(s);
|
|
51
|
+
const zoom = sel('[data-topo-chrome-zoom-level]');
|
|
52
|
+
return {
|
|
53
|
+
zoomCls: zoom?.className ?? '',
|
|
54
|
+
zoomFw: zoom ? getComputedStyle(zoom).fontWeight : null,
|
|
55
|
+
zoomText: zoom?.textContent?.trim() ?? null,
|
|
56
|
+
recentCountFw: sel('[data-recent-panel-count]')?.getAttribute('font-weight') ?? null,
|
|
57
|
+
legendCountFw: sel('[data-legend-panel-count]')?.getAttribute('font-weight') ?? null,
|
|
58
|
+
rowWorkingFw: sel('[data-legend-count="working"]')?.getAttribute('font-weight') ?? null,
|
|
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
|
+
zoom_cls_has_font_medium: probe.zoomCls.includes('font-medium'),
|
|
67
|
+
zoom_cls_has_tabular: probe.zoomCls.includes('tabular-nums'),
|
|
68
|
+
zoom_computed_fw_500: String(probe.zoomFw) === '500',
|
|
69
|
+
zoom_text_percent: /\d+%$/.test(probe.zoomText || ''),
|
|
70
|
+
r311_recent_count_fw_600: probe.recentCountFw === '600',
|
|
71
|
+
r310_legend_count_fw_600: probe.legendCountFw === '600',
|
|
72
|
+
r309_row_working_fw_600: probe.rowWorkingFw === '600',
|
|
73
|
+
r304_subhint_ls_0_15: probe.subhintLs === '0.15',
|
|
74
|
+
r294_pulse_absent: probe.pulseCount === 0,
|
|
75
|
+
};
|
|
76
|
+
const ok = Object.values(results).every(Boolean);
|
|
77
|
+
console.log(`${ok ? '✅' : '❌'} chrome zoom readout weight:`, JSON.stringify(results),
|
|
78
|
+
'\n zoom readout:', probe.zoomText, 'fw=', probe.zoomFw,
|
|
79
|
+
'\n SVG panel counts (R310/R311):', probe.legendCountFw, '/', probe.recentCountFw,
|
|
80
|
+
'\n row count (R309):', probe.rowWorkingFw);
|
|
81
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/* Round 227 verification: click-ripple SMIL <animate> gets ease-out
|
|
2
|
+
* curve via calcMode/keySplines instead of default linear
|
|
3
|
+
* interpolation. Click triggers ripple state (R14 node click); test
|
|
4
|
+
* probes the resulting <circle data-click-ripple> element's two
|
|
5
|
+
* <animate> children for the spline attributes.
|
|
6
|
+
*
|
|
7
|
+
* Test scenario:
|
|
8
|
+
* - Load TopoGraph with 4 nodes
|
|
9
|
+
* - Click the first node (R14 click-feel state machine)
|
|
10
|
+
* - Within 500ms (before ripple expires), probe [data-click-ripple]
|
|
11
|
+
* - Verify both <animate> children have:
|
|
12
|
+
* calcMode="spline"
|
|
13
|
+
* keyTimes="0;1"
|
|
14
|
+
* keySplines="0.25 0.1 0.25 1"
|
|
15
|
+
* dur="0.5s"
|
|
16
|
+
* fill="freeze"
|
|
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
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
24
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
25
|
+
await ctx.addInitScript(() => {
|
|
26
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
27
|
+
});
|
|
28
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
29
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
30
|
+
const r = await route.fetch();
|
|
31
|
+
const b = await r.json();
|
|
32
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
33
|
+
const mk = (alias) => ({
|
|
34
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
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('alpha'), mk('beta'), mk('gamma'), mk('delta'),
|
|
40
|
+
] } });
|
|
41
|
+
});
|
|
42
|
+
await ctx.route('**/api/hub/messages*', (r2) => r2.fulfill({ json: { messages: [] } }));
|
|
43
|
+
await ctx.route('**/api/hub/tasks*', (r2) => r2.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.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
48
|
+
await page.waitForTimeout(400);
|
|
49
|
+
|
|
50
|
+
// Click the first node body (its <circle data-node-status-ring> sits
|
|
51
|
+
// inside the node <g>, which has the onClick handler that fires
|
|
52
|
+
// setClickRipple). Programmatic dispatch — Playwright .click() can
|
|
53
|
+
// be flaky on SVG <g>, so we synthesize the event directly.
|
|
54
|
+
await page.evaluate(() => {
|
|
55
|
+
const node = document.querySelector('g[data-node]');
|
|
56
|
+
if (!node) throw new Error('no node found');
|
|
57
|
+
node.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
|
|
58
|
+
});
|
|
59
|
+
// Ripple element mounts on next React commit — wait for it.
|
|
60
|
+
await page.waitForSelector('[data-click-ripple]', { timeout: 3000, state: 'attached' });
|
|
61
|
+
|
|
62
|
+
const probe = await page.evaluate(() => {
|
|
63
|
+
const ripple = document.querySelector('[data-click-ripple]');
|
|
64
|
+
if (!ripple) return null;
|
|
65
|
+
const animates = ripple.querySelectorAll('animate');
|
|
66
|
+
return Array.from(animates).map((a) => ({
|
|
67
|
+
attr: a.getAttribute('attributeName'),
|
|
68
|
+
calcMode: a.getAttribute('calcMode'),
|
|
69
|
+
keyTimes: a.getAttribute('keyTimes'),
|
|
70
|
+
keySplines: a.getAttribute('keySplines'),
|
|
71
|
+
dur: a.getAttribute('dur'),
|
|
72
|
+
fill: a.getAttribute('fill'),
|
|
73
|
+
}));
|
|
74
|
+
});
|
|
75
|
+
await browser.close();
|
|
76
|
+
|
|
77
|
+
const easeSpline = '0.25 0.1 0.25 1';
|
|
78
|
+
const expected = ['r', 'opacity'];
|
|
79
|
+
|
|
80
|
+
const results = {
|
|
81
|
+
ripple_mounted: probe !== null,
|
|
82
|
+
two_animates: probe?.length === 2,
|
|
83
|
+
has_r_animate: probe?.some(a => a.attr === 'r') ?? false,
|
|
84
|
+
has_opacity_animate: probe?.some(a => a.attr === 'opacity') ?? false,
|
|
85
|
+
both_spline_calcmode: probe?.every(a => a.calcMode === 'spline') ?? false,
|
|
86
|
+
both_keytimes_0_1: probe?.every(a => a.keyTimes === '0;1') ?? false,
|
|
87
|
+
both_ease_keysplines: probe?.every(a => a.keySplines === easeSpline) ?? false,
|
|
88
|
+
both_dur_0_5s: probe?.every(a => a.dur === '0.5s') ?? false,
|
|
89
|
+
both_fill_freeze: probe?.every(a => a.fill === 'freeze') ?? false,
|
|
90
|
+
attrs_match_expected: probe?.map(a => a.attr).sort().join(',') === expected.sort().join(','),
|
|
91
|
+
};
|
|
92
|
+
const ok = Object.values(results).every(Boolean);
|
|
93
|
+
console.log(`${ok ? '✅' : '❌'} click-ripple ease-out:`, JSON.stringify(results), '\n probe:', probe);
|
|
94
|
+
process.exit(ok ? 0 : 1);
|