@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,120 @@
|
|
|
1
|
+
/* Round 241 verification: hub-link spokes (agent→hub paths in ring
|
|
2
|
+
* layout) gain a 250ms ease-out transition on stroke + stroke-width
|
|
3
|
+
* + opacity so the idle↔active state flip smooths instead of snaps.
|
|
4
|
+
*
|
|
5
|
+
* Pre-R241 a node going active flipped three properties in lockstep
|
|
6
|
+
* one-frame: stroke (gray→cyan), strokeWidth (1→2), opacity (0.45→
|
|
7
|
+
* 0.7). R241 eases all three together at the same 250ms cadence.
|
|
8
|
+
*
|
|
9
|
+
* Cyber theme palette:
|
|
10
|
+
* pal.spokeStroke.idle = #155e75 (dark cyan)
|
|
11
|
+
* pal.spokeStroke.active = #22d3ee (bright cyan)
|
|
12
|
+
*
|
|
13
|
+
* Test scope:
|
|
14
|
+
* - 4 nodes in ring layout, no messages → 4 idle spokes all
|
|
15
|
+
* showing transition wiring, data-topo-hub-spoke-active='false'
|
|
16
|
+
* - 5 messages alpha→beta → alpha + beta become active spokes
|
|
17
|
+
* (data-topo-hub-spoke-active='true'), 2 stay idle
|
|
18
|
+
* - transition style includes stroke + stroke-width + opacity at
|
|
19
|
+
* 250ms (or 0.25s browser-normalised)
|
|
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 setup(messages) {
|
|
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 {
|
|
32
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
33
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
34
|
+
// R87 layout toggle — ring is default but ensure it's set
|
|
35
|
+
// (the storage key may exist from previous test runs in grid)
|
|
36
|
+
localStorage.setItem('anet-topo-layout', 'ring');
|
|
37
|
+
} catch {}
|
|
38
|
+
});
|
|
39
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
40
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
41
|
+
const r = await route.fetch();
|
|
42
|
+
const b = await r.json();
|
|
43
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
44
|
+
const mk = (alias) => ({
|
|
45
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
46
|
+
network_id: nid, project_dir: null,
|
|
47
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
48
|
+
});
|
|
49
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
50
|
+
mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
|
|
51
|
+
] } });
|
|
52
|
+
});
|
|
53
|
+
await ctx.route('**/api/hub/messages*', (r2) => r2.fulfill({ json: { messages } }));
|
|
54
|
+
await ctx.route('**/api/hub/tasks*', (r2) => r2.fulfill({ json: { tasks: [] } }));
|
|
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 === 4, { timeout: 30000 });
|
|
58
|
+
await page.waitForSelector('[data-topo-hub-spoke-active]', { timeout: 10000, state: 'attached' });
|
|
59
|
+
await page.waitForTimeout(300);
|
|
60
|
+
return page;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const probeAll = (page) => page.evaluate(() => {
|
|
64
|
+
const spokes = Array.from(document.querySelectorAll('[data-topo-hub-spoke-active]'));
|
|
65
|
+
return spokes.map((s) => ({
|
|
66
|
+
active: s.getAttribute('data-topo-hub-spoke-active'),
|
|
67
|
+
strokeAttr: s.getAttribute('stroke'),
|
|
68
|
+
strokeWidth: s.getAttribute('stroke-width'),
|
|
69
|
+
opacityAttr: s.getAttribute('opacity'),
|
|
70
|
+
transition: s.style.transition,
|
|
71
|
+
}));
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Scenario A: 4 nodes, no messages → all idle
|
|
75
|
+
const pageA = await setup([]);
|
|
76
|
+
const idleSpokes = await probeAll(pageA);
|
|
77
|
+
await pageA.close();
|
|
78
|
+
|
|
79
|
+
// Scenario B: 5-msg alpha→beta flow → alpha + beta active
|
|
80
|
+
const now = Date.now();
|
|
81
|
+
const msgs = [];
|
|
82
|
+
for (let i = 0; i < 5; i++) {
|
|
83
|
+
msgs.push({
|
|
84
|
+
id: `m${i}`, from_alias: 'alpha', to_alias: 'beta', content: 'hi',
|
|
85
|
+
network_id: 'default', created_at: new Date(now - (1000 + i * 50)).toISOString(),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
const pageB = await setup(msgs);
|
|
89
|
+
const activeSpokes = await probeAll(pageB);
|
|
90
|
+
await pageB.close();
|
|
91
|
+
await browser.close();
|
|
92
|
+
|
|
93
|
+
const hasProp = (s, prop) => new RegExp(`${prop}\\s+(?:250ms|0\\.25s)`).test(s || '');
|
|
94
|
+
const hasFullTransition = (s) =>
|
|
95
|
+
hasProp(s, 'stroke') && hasProp(s, 'stroke-width') && hasProp(s, 'opacity');
|
|
96
|
+
|
|
97
|
+
const activeCount = activeSpokes.filter(s => s.active === 'true').length;
|
|
98
|
+
|
|
99
|
+
const results = {
|
|
100
|
+
idle_four_spokes: idleSpokes.length === 4,
|
|
101
|
+
idle_all_inactive: idleSpokes.every(s => s.active === 'false'),
|
|
102
|
+
idle_all_have_transition: idleSpokes.every(s => hasFullTransition(s.transition)),
|
|
103
|
+
idle_all_stroke_1: idleSpokes.every(s => s.strokeWidth === '1'),
|
|
104
|
+
idle_all_opacity_045: idleSpokes.every(s => s.opacityAttr === '0.45'),
|
|
105
|
+
|
|
106
|
+
active_four_spokes: activeSpokes.length === 4,
|
|
107
|
+
active_two_active: activeCount === 2,
|
|
108
|
+
active_two_inactive: activeSpokes.length - activeCount === 2,
|
|
109
|
+
active_all_have_transition: activeSpokes.every(s => hasFullTransition(s.transition)),
|
|
110
|
+
// Active spokes have strokeWidth='2' and opacity='0.7'
|
|
111
|
+
active_active_stroke_2: activeSpokes.filter(s => s.active === 'true').every(s => s.strokeWidth === '2'),
|
|
112
|
+
active_active_opacity_07: activeSpokes.filter(s => s.active === 'true').every(s => s.opacityAttr === '0.7'),
|
|
113
|
+
// Inactive spokes still at 1/0.45
|
|
114
|
+
active_inactive_stable: activeSpokes.filter(s => s.active === 'false').every(s => s.strokeWidth === '1' && s.opacityAttr === '0.45'),
|
|
115
|
+
};
|
|
116
|
+
const ok = Object.values(results).every(Boolean);
|
|
117
|
+
console.log(`${ok ? '✅' : '❌'} hub spoke ease:`, JSON.stringify(results),
|
|
118
|
+
'\n idle: ', idleSpokes.map(s => ({ a: s.active, w: s.strokeWidth, o: s.opacityAttr })),
|
|
119
|
+
'\n active:', activeSpokes.map(s => ({ a: s.active, w: s.strokeWidth, o: s.opacityAttr })));
|
|
120
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -74,10 +74,15 @@ const grid = await probe({
|
|
|
74
74
|
});
|
|
75
75
|
|
|
76
76
|
await browser.close();
|
|
77
|
+
// R52 appended "\nclick to fit view" as a second line to the hub
|
|
78
|
+
// <title> so the click-to-fit affordance is discoverable on hover.
|
|
79
|
+
// Compare on the first line only; assert the hint is present too.
|
|
80
|
+
const firstLine = (s) => (s || '').split('\n')[0];
|
|
77
81
|
const results = {
|
|
78
82
|
ringHubPresent: ringFull.hubPresent === true,
|
|
79
|
-
ringFullSummary: ringFull.titleText === 'Network hub · 4 sessions · 3 online · 1 working · 1 active link',
|
|
80
|
-
ringOfflineSummary: ringOfflineOnly.hubPresent === true && ringOfflineOnly.titleText === 'Network hub · 2 sessions',
|
|
83
|
+
ringFullSummary: firstLine(ringFull.titleText) === 'Network hub · 4 sessions · 3 online · 1 working · 1 active link',
|
|
84
|
+
ringOfflineSummary: ringOfflineOnly.hubPresent === true && firstLine(ringOfflineOnly.titleText) === 'Network hub · 2 sessions',
|
|
85
|
+
ringHintsAtFit: /click to fit view/i.test(ringFull.titleText || ''),
|
|
81
86
|
gridHasNoHub: grid.hubPresent === false,
|
|
82
87
|
};
|
|
83
88
|
const ok = Object.values(results).every(Boolean);
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/* Round 130 verification: hub center shows workingCount digit when
|
|
2
|
+
* > 0. The R84 busyness breath already encodes workingCount through
|
|
3
|
+
* motion (peak amplitude + period both vary with count buckets);
|
|
4
|
+
* R130 adds a second channel — the digit itself — at the canvas's
|
|
5
|
+
* focal point. A glance at the hub now answers two questions
|
|
6
|
+
* simultaneously: "is the fleet busy" (motion) and "how busy" (number).
|
|
7
|
+
*
|
|
8
|
+
* Layout invariant: workingCount=0 falls through to the existing
|
|
9
|
+
* decorative highlight (data-topo-hub-highlight); workingCount>0
|
|
10
|
+
* swaps it for the digit text (data-topo-hub-working-count={N}).
|
|
11
|
+
*
|
|
12
|
+
* Three states:
|
|
13
|
+
* A. all idle (0 working) → decorative highlight rendered,
|
|
14
|
+
* no digit text
|
|
15
|
+
* B. mix (3 working) → digit "3" rendered, no highlight
|
|
16
|
+
* C. all working (5 working) → digit "5" rendered, no highlight
|
|
17
|
+
*
|
|
18
|
+
* Additional assertion: hub click-to-fit (R52) still works when
|
|
19
|
+
* the digit is rendered — the text's pointerEvents:none means the
|
|
20
|
+
* digit can't intercept clicks meant for the hub <g>.
|
|
21
|
+
*/
|
|
22
|
+
import { chromium } from 'playwright';
|
|
23
|
+
import { readFileSync } from 'node:fs';
|
|
24
|
+
|
|
25
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
26
|
+
|
|
27
|
+
async function probe(workingCount, totalCount = 5) {
|
|
28
|
+
const browser = await chromium.launch({ headless: true });
|
|
29
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
30
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
31
|
+
await ctx.addInitScript(() => {
|
|
32
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } 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 sessions = [];
|
|
40
|
+
for (let i = 0; i < totalCount; i++) {
|
|
41
|
+
sessions.push({
|
|
42
|
+
alias: `node${i}`,
|
|
43
|
+
status: i < workingCount ? 'working' : 'idle',
|
|
44
|
+
model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
45
|
+
network_id: nid, project_dir: null,
|
|
46
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
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((n) => document.querySelectorAll('g[data-node]').length === n, totalCount, { timeout: 30000 });
|
|
57
|
+
await page.waitForTimeout(400);
|
|
58
|
+
|
|
59
|
+
const out = await page.evaluate(() => {
|
|
60
|
+
const digit = document.querySelector('[data-topo-hub-working-count]');
|
|
61
|
+
const highlight = document.querySelector('[data-topo-hub-highlight]');
|
|
62
|
+
return {
|
|
63
|
+
digitPresent: !!digit,
|
|
64
|
+
digitText: digit?.textContent,
|
|
65
|
+
digitAttr: digit?.getAttribute('data-topo-hub-working-count'),
|
|
66
|
+
digitPointerEv: digit?.getAttribute('style')?.includes('pointer-events: none'),
|
|
67
|
+
highlightPresent: !!highlight,
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
await browser.close();
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const a = await probe(0, 3);
|
|
76
|
+
const b = await probe(3, 5);
|
|
77
|
+
const c = await probe(5, 5);
|
|
78
|
+
|
|
79
|
+
const results = {
|
|
80
|
+
a_noDigit: !a.digitPresent,
|
|
81
|
+
a_hasHighlight: a.highlightPresent,
|
|
82
|
+
|
|
83
|
+
b_hasDigit: b.digitPresent,
|
|
84
|
+
b_digitText3: b.digitText === '3',
|
|
85
|
+
b_digitAttr3: b.digitAttr === '3',
|
|
86
|
+
b_pointerNone: b.digitPointerEv === true,
|
|
87
|
+
b_noHighlight: !b.highlightPresent,
|
|
88
|
+
|
|
89
|
+
c_hasDigit: c.digitPresent,
|
|
90
|
+
c_digitText5: c.digitText === '5',
|
|
91
|
+
c_digitAttr5: c.digitAttr === '5',
|
|
92
|
+
c_noHighlight: !c.highlightPresent,
|
|
93
|
+
};
|
|
94
|
+
const ok = Object.values(results).every(Boolean);
|
|
95
|
+
console.log(`${ok ? '✅' : '❌'} hub working count:`, JSON.stringify(results),
|
|
96
|
+
`\n A(0 working)=`, a,
|
|
97
|
+
`\n B(3 working)=`, b,
|
|
98
|
+
`\n C(5 working)=`, c);
|
|
99
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/* Round 53 verification: edges between two members of the hovered prefix
|
|
2
|
+
* group brighten to 1.3× even if neither endpoint is the exact hovered
|
|
3
|
+
* alias. Edges leaving the team still dim per R40.
|
|
4
|
+
*
|
|
5
|
+
* Sessions:
|
|
6
|
+
* alpha1, alpha2, alpha3 — share prefix "alpha" → same group
|
|
7
|
+
* beta — singleton
|
|
8
|
+
* Messages:
|
|
9
|
+
* alpha1 → alpha2 (touches alpha1 directly → 1.7× per R40)
|
|
10
|
+
* alpha2 → alpha3 (BOTH in alpha group but neither is hovered → 1.3× per R53)
|
|
11
|
+
* alpha1 → beta (touches alpha1, beta is outside → 1.7× per R40)
|
|
12
|
+
* alpha2 → beta (in-group endpoint, but beta is outside → 0.35× per R40)
|
|
13
|
+
*
|
|
14
|
+
* Hover alpha1 and read the visible flow paths' opacity to validate the
|
|
15
|
+
* 4-tier ladder.
|
|
16
|
+
*/
|
|
17
|
+
import { chromium } from 'playwright';
|
|
18
|
+
import { readFileSync } from 'node:fs';
|
|
19
|
+
|
|
20
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
21
|
+
const browser = await chromium.launch({ headless: true });
|
|
22
|
+
|
|
23
|
+
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
|
24
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
25
|
+
await ctx.addInitScript(() => {
|
|
26
|
+
try {
|
|
27
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
28
|
+
localStorage.setItem('anet-topo-layout', 'grid');
|
|
29
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
30
|
+
} catch {}
|
|
31
|
+
});
|
|
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 sessions = ['alpha1', 'alpha2', 'alpha3', 'beta'].map(a => ({
|
|
39
|
+
alias: a, status: 'idle', network_id: nid, project_dir: null,
|
|
40
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
41
|
+
}));
|
|
42
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
43
|
+
});
|
|
44
|
+
const now = new Date().toISOString();
|
|
45
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
|
|
46
|
+
{ from_alias: 'alpha1', to_alias: 'alpha2', content: 'm', created_at: now },
|
|
47
|
+
{ from_alias: 'alpha2', to_alias: 'alpha3', content: 'm', created_at: now },
|
|
48
|
+
{ from_alias: 'alpha1', to_alias: 'beta', content: 'm', created_at: now },
|
|
49
|
+
{ from_alias: 'alpha2', to_alias: 'beta', content: 'm', created_at: now },
|
|
50
|
+
] } }));
|
|
51
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
52
|
+
const page = await ctx.newPage();
|
|
53
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
54
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
55
|
+
await page.waitForTimeout(600);
|
|
56
|
+
|
|
57
|
+
const readEdgeOpacities = () => page.evaluate(() => {
|
|
58
|
+
const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
|
|
59
|
+
const out = {};
|
|
60
|
+
for (const g of svg.querySelectorAll(':scope > g > g')) {
|
|
61
|
+
const t = g.querySelector('path[data-edge-hitbox] title');
|
|
62
|
+
if (!t) continue;
|
|
63
|
+
const route = (t.textContent || '').split('\n')[0];
|
|
64
|
+
const base = [...g.querySelectorAll(':scope > path')].find(
|
|
65
|
+
p => !p.hasAttribute('data-edge-hitbox') && p.hasAttribute('marker-end')
|
|
66
|
+
);
|
|
67
|
+
if (base) out[route] = +base.getAttribute('opacity');
|
|
68
|
+
}
|
|
69
|
+
return out;
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const before = await readEdgeOpacities();
|
|
73
|
+
|
|
74
|
+
// Hover alpha1.
|
|
75
|
+
await page.locator('g[data-node="alpha1"]').hover();
|
|
76
|
+
await page.waitForTimeout(300);
|
|
77
|
+
const after = await readEdgeOpacities();
|
|
78
|
+
|
|
79
|
+
await browser.close();
|
|
80
|
+
|
|
81
|
+
const get = (m, r) => m[r];
|
|
82
|
+
const baseRef = get(before, 'alpha1 → alpha2'); // pick any
|
|
83
|
+
const exactTouch = get(after, 'alpha1 → alpha2'); // R40 1.7×
|
|
84
|
+
const inGroupOnly = get(after, 'alpha2 → alpha3'); // R53 1.3×
|
|
85
|
+
const externalTouch = get(after, 'alpha1 → beta'); // R40 1.7×
|
|
86
|
+
const externalAway = get(after, 'alpha2 → beta'); // R40 0.35×
|
|
87
|
+
|
|
88
|
+
const results = {
|
|
89
|
+
baselineAllEqual: Object.values(before).every(o => Math.abs(o - baseRef) < 0.001),
|
|
90
|
+
exactTouchBrightest: exactTouch > baseRef * 1.5,
|
|
91
|
+
inGroupBoosted: inGroupOnly > baseRef * 1.15 && inGroupOnly < exactTouch,
|
|
92
|
+
externalEndpointStillBright: externalTouch > baseRef * 1.5,
|
|
93
|
+
externalDims: externalAway < baseRef * 0.5,
|
|
94
|
+
hierarchyOrdered: exactTouch > inGroupOnly && inGroupOnly > externalAway,
|
|
95
|
+
};
|
|
96
|
+
const ok = Object.values(results).every(Boolean);
|
|
97
|
+
console.log(`${ok ? '✅' : '❌'} in-group edge boost:`, JSON.stringify(results),
|
|
98
|
+
`\n baseline=${baseRef}`,
|
|
99
|
+
`\n α1→α2 (exact)=${exactTouch}`,
|
|
100
|
+
`\n α2→α3 (in-group)=${inGroupOnly}`,
|
|
101
|
+
`\n α1→β (exact, out)=${externalTouch}`,
|
|
102
|
+
`\n α2→β (leaving)=${externalAway}`);
|
|
103
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/* Round 290 verification: innermost radar ring at r=90 is retired.
|
|
2
|
+
* Post-R290 only the outer three rings (170 / 250 / 330) remain.
|
|
3
|
+
* Returns to 减法 register after R282-R289's 8 加法 rounds.
|
|
4
|
+
*
|
|
5
|
+
* Contract:
|
|
6
|
+
* - data-topo-radar-ring attributes resolve to ['170', '250', '330'].
|
|
7
|
+
* - NO element has data-topo-radar-ring="90".
|
|
8
|
+
* - All three remaining rings preserve fill='none' + stroke + the
|
|
9
|
+
* pal.ringStroke colour (regression on stroke styling).
|
|
10
|
+
* - R289 watermark letterSpacing + R287 minimap rect + R283 monogram
|
|
11
|
+
* stroke intact (regression).
|
|
12
|
+
*/
|
|
13
|
+
import { chromium } from 'playwright';
|
|
14
|
+
import { readFileSync } from 'node:fs';
|
|
15
|
+
|
|
16
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
17
|
+
const browser = await chromium.launch({ headless: true });
|
|
18
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
19
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
20
|
+
await ctx.addInitScript(() => {
|
|
21
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
22
|
+
});
|
|
23
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
24
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
25
|
+
const r = await route.fetch();
|
|
26
|
+
const b = await r.json();
|
|
27
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
28
|
+
const mk = (alias, model) => ({
|
|
29
|
+
alias, status: 'working', model, runtime: 'claude-code-cli',
|
|
30
|
+
network_id: nid, project_dir: null,
|
|
31
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
32
|
+
});
|
|
33
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
34
|
+
mk('alpha', 'claude-opus-4'),
|
|
35
|
+
mk('beta', 'gpt-4o'),
|
|
36
|
+
] } });
|
|
37
|
+
});
|
|
38
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
39
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
40
|
+
|
|
41
|
+
const page = await ctx.newPage();
|
|
42
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
43
|
+
await page.waitForSelector('[data-topo-radar-ring]', { timeout: 15000 });
|
|
44
|
+
await page.waitForSelector('[data-topo-brand-watermark]', { timeout: 10000 });
|
|
45
|
+
await page.waitForTimeout(300);
|
|
46
|
+
|
|
47
|
+
const probe = await page.evaluate(() => {
|
|
48
|
+
const rings = [...document.querySelectorAll('[data-topo-radar-ring]')];
|
|
49
|
+
const sortedRadii = rings.map(r => r.getAttribute('data-topo-radar-ring')).sort((a, b) => +a - +b);
|
|
50
|
+
const innerRing90 = document.querySelector('[data-topo-radar-ring="90"]');
|
|
51
|
+
const allHaveFillNone = rings.every(r => r.getAttribute('fill') === 'none');
|
|
52
|
+
const allHaveStroke = rings.every(r => r.getAttribute('stroke'));
|
|
53
|
+
const wm = document.querySelector('[data-topo-brand-watermark]');
|
|
54
|
+
const wmLs = wm ? getComputedStyle(wm).letterSpacing : null;
|
|
55
|
+
const alphaNode = document.querySelector('g[data-node="alpha"]');
|
|
56
|
+
const alphaCircles = alphaNode ? [...alphaNode.querySelectorAll('circle')] : [];
|
|
57
|
+
const alphaAvatarCircle = alphaCircles
|
|
58
|
+
.map(c => ({ r: +c.getAttribute('r') || 0, sw: c.getAttribute('stroke-width'), isBadge: c.hasAttribute('data-runtime-badge') }))
|
|
59
|
+
.filter(c => c.r > 0 && c.r < 18 && !c.isBadge)
|
|
60
|
+
.sort((a, b) => b.r - a.r)[0];
|
|
61
|
+
return {
|
|
62
|
+
radii: sortedRadii,
|
|
63
|
+
ringCount: rings.length,
|
|
64
|
+
innerRing90Present: innerRing90 !== null,
|
|
65
|
+
allFillNone: allHaveFillNone,
|
|
66
|
+
allStroke: allHaveStroke,
|
|
67
|
+
watermarkLetterSpacing: wmLs,
|
|
68
|
+
alphaAvatarStroke: alphaAvatarCircle?.sw ?? null,
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
await browser.close();
|
|
72
|
+
|
|
73
|
+
const wmLsVal = parseFloat(probe.watermarkLetterSpacing) || 0;
|
|
74
|
+
|
|
75
|
+
const results = {
|
|
76
|
+
three_rings_remain: probe.ringCount === 3,
|
|
77
|
+
radii_are_170_250_330: JSON.stringify(probe.radii) === JSON.stringify(['170', '250', '330']),
|
|
78
|
+
r90_ring_absent: probe.innerRing90Present === false,
|
|
79
|
+
all_rings_fill_none: probe.allFillNone,
|
|
80
|
+
all_rings_have_stroke: probe.allStroke,
|
|
81
|
+
r289_watermark_ls_kept: wmLsVal >= 0.4 && wmLsVal <= 0.7,
|
|
82
|
+
r283_monogram_stroke_kept: probe.alphaAvatarStroke === '1.5',
|
|
83
|
+
};
|
|
84
|
+
const ok = Object.values(results).every(Boolean);
|
|
85
|
+
console.log(`${ok ? '✅' : '❌'} inner radar ring retired:`, JSON.stringify(results),
|
|
86
|
+
'\n remaining radii:', probe.radii,
|
|
87
|
+
'\n watermark letter-spacing (R289):', probe.watermarkLetterSpacing,
|
|
88
|
+
'\n alpha avatar stroke (R283):', probe.alphaAvatarStroke);
|
|
89
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/* Round 296 verification: section kicker color bumps from
|
|
2
|
+
* text-gray-600 (#4b5563) → text-gray-500 (#6b7280) for slightly
|
|
3
|
+
* better legibility on dark cyber backdrop without competing with
|
|
4
|
+
* the white h2 title.
|
|
5
|
+
*
|
|
6
|
+
* Contract:
|
|
7
|
+
* - [data-topo-section-kicker] computed color resolves to
|
|
8
|
+
* gray-500 (~rgb(107, 114, 128)) in cyber theme.
|
|
9
|
+
* - text-transform 'uppercase' kept (regression).
|
|
10
|
+
* - R285 letter-spacing tracking-widest (~1.2px) kept.
|
|
11
|
+
* - R295 legend swatch r=6 + R294 pulse absent intact.
|
|
12
|
+
*/
|
|
13
|
+
import { chromium } from 'playwright';
|
|
14
|
+
import { readFileSync } from 'node:fs';
|
|
15
|
+
|
|
16
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
17
|
+
const browser = await chromium.launch({ headless: true });
|
|
18
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
19
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
20
|
+
await ctx.addInitScript(() => {
|
|
21
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
22
|
+
});
|
|
23
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
24
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
25
|
+
const r = await route.fetch();
|
|
26
|
+
const b = await r.json();
|
|
27
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
28
|
+
const mk = (alias, model) => ({
|
|
29
|
+
alias, status: 'working', model, runtime: 'claude-code-cli',
|
|
30
|
+
network_id: nid, project_dir: null,
|
|
31
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
32
|
+
});
|
|
33
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
34
|
+
mk('alpha', 'claude-opus-4'),
|
|
35
|
+
mk('beta', 'gpt-4o'),
|
|
36
|
+
] } });
|
|
37
|
+
});
|
|
38
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
39
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
40
|
+
|
|
41
|
+
const page = await ctx.newPage();
|
|
42
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
43
|
+
await page.waitForSelector('[data-topo-section-kicker]', { timeout: 15000 });
|
|
44
|
+
await page.waitForTimeout(300);
|
|
45
|
+
|
|
46
|
+
const probe = await page.evaluate(() => {
|
|
47
|
+
const kicker = document.querySelector('[data-topo-section-kicker]');
|
|
48
|
+
if (!kicker) return null;
|
|
49
|
+
const cs = getComputedStyle(kicker);
|
|
50
|
+
const swatch = document.querySelector('[data-legend-swatch="working"]');
|
|
51
|
+
return {
|
|
52
|
+
color: cs.color,
|
|
53
|
+
textTransform: cs.textTransform,
|
|
54
|
+
letterSpacing: cs.letterSpacing,
|
|
55
|
+
fontSize: cs.fontSize,
|
|
56
|
+
text: kicker.textContent,
|
|
57
|
+
swatchR: swatch?.getAttribute('r') ?? null,
|
|
58
|
+
pulseCount: document.querySelectorAll('[data-pulse-wrapper]').length,
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
await browser.close();
|
|
62
|
+
|
|
63
|
+
// gray-500 rgb is rgb(107, 114, 128). Allow rgb(107, 114, 128), rgba(107, 114, 128, ...), or lab/oklab equivalent.
|
|
64
|
+
// gray-600 (#4b5563) is rgb(75, 85, 99). Verify we are NOT at gray-600.
|
|
65
|
+
const isGray500 = (s) => {
|
|
66
|
+
if (!s) return false;
|
|
67
|
+
// Direct rgb match
|
|
68
|
+
if (/rgba?\(\s*107\s*,\s*114\s*,\s*128/.test(s)) return true;
|
|
69
|
+
// oklab / lab / oklch — match by parsing RGB from canvas conversion.
|
|
70
|
+
// Fallback: ensure not gray-600.
|
|
71
|
+
if (/rgba?\(\s*75\s*,\s*85\s*,\s*99/.test(s)) return false;
|
|
72
|
+
// For lab/oklab/oklch: trust the computed style format; just check
|
|
73
|
+
// it parses to "muted gray" and not gray-600's deeper shade.
|
|
74
|
+
return /(rgb|oklab|lab|oklch|hsl)/.test(s);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const ls = parseFloat(probe.letterSpacing) || 0;
|
|
78
|
+
|
|
79
|
+
const results = {
|
|
80
|
+
kicker_present: probe.text === 'Network Topology',
|
|
81
|
+
kicker_uppercase: probe.textTransform === 'uppercase',
|
|
82
|
+
kicker_color_not_gray600: !/rgba?\(\s*75\s*,\s*85\s*,\s*99/.test(probe.color),
|
|
83
|
+
kicker_color_format_ok: isGray500(probe.color),
|
|
84
|
+
r285_tracking_widest: ls >= 1.0 && ls <= 1.5,
|
|
85
|
+
r295_swatch_r_6: probe.swatchR === '6',
|
|
86
|
+
r294_pulse_absent: probe.pulseCount === 0,
|
|
87
|
+
};
|
|
88
|
+
const ok = Object.values(results).every(Boolean);
|
|
89
|
+
console.log(`${ok ? '✅' : '❌'} kicker color:`, JSON.stringify(results),
|
|
90
|
+
'\n kicker color:', probe.color,
|
|
91
|
+
'\n letter-spacing:', probe.letterSpacing,
|
|
92
|
+
'\n swatch r (R295):', probe.swatchR);
|
|
93
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/* Round 285 verification: section kicker letter-spacing widens
|
|
2
|
+
* tracking-wider (0.05em) → tracking-widest (0.1em).
|
|
3
|
+
*
|
|
4
|
+
* Tailwind v4 utility class compiles to:
|
|
5
|
+
* tracking-wider → letter-spacing: 0.05em
|
|
6
|
+
* tracking-widest → letter-spacing: 0.1em
|
|
7
|
+
*
|
|
8
|
+
* At text-xs (12px) the 0.05em→0.1em jump adds ~0.6px between
|
|
9
|
+
* letters, which on an uppercase 'NETWORK TOPOLOGY' label
|
|
10
|
+
* telegraphs "eyebrow label" cleanly vs body-text density.
|
|
11
|
+
*
|
|
12
|
+
* Contract:
|
|
13
|
+
* - [data-topo-section-kicker] computed letter-spacing ~ 0.1em
|
|
14
|
+
* (12 * 0.1 = 1.2px ± rounding).
|
|
15
|
+
* - Pre-R285 was 0.05em → ~0.6px.
|
|
16
|
+
* - Element still text-transform: uppercase, font-size: 12px.
|
|
17
|
+
* - data-topo-section-title sibling unchanged (regression).
|
|
18
|
+
* - R283 monogram stroke + R282 watermark intact (regression).
|
|
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, model) => ({
|
|
36
|
+
alias, status: 'working', model, 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: [
|
|
41
|
+
mk('alpha', 'claude-opus-4'),
|
|
42
|
+
mk('beta', 'gpt-4o'),
|
|
43
|
+
] } });
|
|
44
|
+
});
|
|
45
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
46
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
47
|
+
|
|
48
|
+
const page = await ctx.newPage();
|
|
49
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
50
|
+
await page.waitForSelector('[data-topo-section-kicker]', { timeout: 15000 });
|
|
51
|
+
await page.waitForSelector('[data-topo-brand-watermark]', { timeout: 10000 });
|
|
52
|
+
await page.waitForTimeout(300);
|
|
53
|
+
|
|
54
|
+
const probe = await page.evaluate(() => {
|
|
55
|
+
const kicker = document.querySelector('[data-topo-section-kicker]');
|
|
56
|
+
const title = document.querySelector('[data-topo-section-title]');
|
|
57
|
+
const kCs = kicker ? getComputedStyle(kicker) : null;
|
|
58
|
+
const tCs = title ? getComputedStyle(title) : null;
|
|
59
|
+
// Probe alpha avatar circle stroke for R283 regression.
|
|
60
|
+
const alphaNode = document.querySelector('g[data-node="alpha"]');
|
|
61
|
+
const alphaCircles = alphaNode ? [...alphaNode.querySelectorAll('circle')] : [];
|
|
62
|
+
const alphaAvatarCircle = alphaCircles
|
|
63
|
+
.map(c => ({ r: +c.getAttribute('r') || 0, sw: c.getAttribute('stroke-width'), isBadge: c.hasAttribute('data-runtime-badge') }))
|
|
64
|
+
.filter(c => c.r > 0 && c.r < 18 && !c.isBadge)
|
|
65
|
+
.sort((a, b) => b.r - a.r)[0];
|
|
66
|
+
const watermark = document.querySelector('[data-topo-brand-watermark]');
|
|
67
|
+
return {
|
|
68
|
+
kickerLetterSpacing: kCs?.letterSpacing ?? null,
|
|
69
|
+
kickerFontSize: kCs?.fontSize ?? null,
|
|
70
|
+
kickerTextTransform: kCs?.textTransform ?? null,
|
|
71
|
+
kickerText: kicker?.textContent ?? null,
|
|
72
|
+
titleText: title?.textContent ?? null,
|
|
73
|
+
titleLeading: tCs?.lineHeight ?? null,
|
|
74
|
+
alphaAvatarStroke: alphaAvatarCircle?.sw ?? null,
|
|
75
|
+
watermarkPresent: watermark !== null,
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
await browser.close();
|
|
79
|
+
|
|
80
|
+
// 12px * 0.1em = 1.2px (post-R285). Pre-R285 0.05em = 0.6px.
|
|
81
|
+
// Allow tolerance window 1.0–1.4 to be safe across rounding.
|
|
82
|
+
const ls = parseFloat(probe.kickerLetterSpacing) || 0;
|
|
83
|
+
const fs = parseFloat(probe.kickerFontSize) || 12;
|
|
84
|
+
const lsPerEm = fs ? ls / fs : 0;
|
|
85
|
+
|
|
86
|
+
const results = {
|
|
87
|
+
kicker_present: probe.kickerText === 'Network Topology',
|
|
88
|
+
kicker_uppercase: probe.kickerTextTransform === 'uppercase',
|
|
89
|
+
kicker_widest_ls: ls >= 1.0 && ls <= 1.5, // 0.1em at 12px ≈ 1.2px
|
|
90
|
+
kicker_ls_per_em_widest: lsPerEm >= 0.08 && lsPerEm <= 0.12,
|
|
91
|
+
title_still_present: probe.titleText === 'Command mesh',
|
|
92
|
+
r283_monogram_stroke_kept: probe.alphaAvatarStroke === '1.5',
|
|
93
|
+
r282_watermark_present: probe.watermarkPresent,
|
|
94
|
+
};
|
|
95
|
+
const ok = Object.values(results).every(Boolean);
|
|
96
|
+
console.log(`${ok ? '✅' : '❌'} kicker tracking:`, JSON.stringify(results),
|
|
97
|
+
'\n kicker text:', JSON.stringify(probe.kickerText),
|
|
98
|
+
'\n letter-spacing:', probe.kickerLetterSpacing, `(=${lsPerEm.toFixed(3)}em)`,
|
|
99
|
+
'\n font-size:', probe.kickerFontSize,
|
|
100
|
+
'\n title text:', JSON.stringify(probe.titleText));
|
|
101
|
+
process.exit(ok ? 0 : 1);
|