@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,125 @@
|
|
|
1
|
+
/* Round 206 verification: active-links chip recedes to opacity 0.5
|
|
2
|
+
* when flowLinks.length === 0, mirroring R204 legend count recede and
|
|
3
|
+
* R205 working/online chip recede patterns at a third scope.
|
|
4
|
+
*
|
|
5
|
+
* Pre-R206 "0 active links" rendered at the same bg-gray-500/10 +
|
|
6
|
+
* opacity 1 as "12 active links" — only the absent cursor + missing
|
|
7
|
+
* tooltip signalled empty. R206 adds the visual recede so all three
|
|
8
|
+
* signals (visual / interactive / affordance) align.
|
|
9
|
+
*
|
|
10
|
+
* Test scenarios:
|
|
11
|
+
* A. 0 messages → flowLinks.length === 0:
|
|
12
|
+
* data-active-links-empty='true', clickable='false',
|
|
13
|
+
* opacity ≈ 0.5
|
|
14
|
+
* B. 3 messages → flowLinks.length === 3:
|
|
15
|
+
* data-active-links-empty='false', clickable='true',
|
|
16
|
+
* opacity = 1.0
|
|
17
|
+
* Both: inline transition includes color/background-color/
|
|
18
|
+
* border-color/opacity 200ms (4-property splice).
|
|
19
|
+
* R193 hover-cyan markup preserved in scenario B className.
|
|
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 { 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: 'cli-claude-code',
|
|
40
|
+
network_id: nid, project_dir: null,
|
|
41
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
42
|
+
});
|
|
43
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
44
|
+
mk('alpha'), 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
|
+
const page = await ctx.newPage();
|
|
50
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
51
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
52
|
+
await page.waitForSelector('[data-active-links-chip]', { timeout: 10000 });
|
|
53
|
+
await page.waitForTimeout(300);
|
|
54
|
+
return page;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const probe = async (page) => page.evaluate(() => {
|
|
58
|
+
const el = document.querySelector('[data-active-links-chip]');
|
|
59
|
+
if (!el) return null;
|
|
60
|
+
const cs = getComputedStyle(el);
|
|
61
|
+
return {
|
|
62
|
+
className: el.getAttribute('class') || '',
|
|
63
|
+
empty: el.getAttribute('data-active-links-empty'),
|
|
64
|
+
clickable: el.getAttribute('data-active-links-clickable'),
|
|
65
|
+
flowCount: el.getAttribute('data-active-links-flow-count'),
|
|
66
|
+
opacity: parseFloat(cs.opacity),
|
|
67
|
+
transition: el.style.transition,
|
|
68
|
+
bg: cs.backgroundColor,
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Scenario A: empty
|
|
73
|
+
const pageA = await setup([]);
|
|
74
|
+
const probeA = await probe(pageA);
|
|
75
|
+
await pageA.close();
|
|
76
|
+
|
|
77
|
+
// Scenario B: populated
|
|
78
|
+
const now = Date.now();
|
|
79
|
+
const pageB = await setup([
|
|
80
|
+
{ id: '1', from_alias: 'alpha', to_alias: 'beta', content: 'hi',
|
|
81
|
+
network_id: 'default', created_at: new Date(now - 1000).toISOString() },
|
|
82
|
+
{ id: '2', from_alias: 'beta', to_alias: 'gamma', content: 'hi',
|
|
83
|
+
network_id: 'default', created_at: new Date(now - 2000).toISOString() },
|
|
84
|
+
{ id: '3', from_alias: 'gamma', to_alias: 'delta', content: 'hi',
|
|
85
|
+
network_id: 'default', created_at: new Date(now - 3000).toISOString() },
|
|
86
|
+
]);
|
|
87
|
+
const probeB = await probe(pageB);
|
|
88
|
+
await pageB.close();
|
|
89
|
+
await browser.close();
|
|
90
|
+
|
|
91
|
+
const near = (got, want, tol = 0.05) => Math.abs(got - want) < tol;
|
|
92
|
+
const has = (s, re) => re.test(s || '');
|
|
93
|
+
|
|
94
|
+
const results = {
|
|
95
|
+
// Scenario A: 0 flows
|
|
96
|
+
A_chip_present: !!probeA,
|
|
97
|
+
A_flow_count_0: probeA?.flowCount === '0',
|
|
98
|
+
A_empty_true: probeA?.empty === 'true',
|
|
99
|
+
A_clickable_false: probeA?.clickable === 'false',
|
|
100
|
+
A_opacity_half: near(probeA?.opacity ?? 0, 0.5),
|
|
101
|
+
// hover variant NOT present on empty (only base bg-gray)
|
|
102
|
+
A_no_hover_variant: !(probeA?.className || '').includes('hover:bg-cyan-500/10'),
|
|
103
|
+
|
|
104
|
+
// Scenario B: 3 flows
|
|
105
|
+
B_chip_present: !!probeB,
|
|
106
|
+
B_flow_count_3: probeB?.flowCount === '3',
|
|
107
|
+
B_empty_false: probeB?.empty === 'false',
|
|
108
|
+
B_clickable_true: probeB?.clickable === 'true',
|
|
109
|
+
B_opacity_full: near(probeB?.opacity ?? 0, 1.0),
|
|
110
|
+
// R193 hover-cyan markup preserved
|
|
111
|
+
B_has_hover_variant: (probeB?.className || '').includes('hover:bg-cyan-500/10'),
|
|
112
|
+
|
|
113
|
+
// Transition integrity — 4-property splice on both scenarios
|
|
114
|
+
A_transition_opacity: has(probeA?.transition, /opacity\s+(?:200ms|0\.2s)/),
|
|
115
|
+
A_transition_bg: has(probeA?.transition, /background-color\s+(?:200ms|0\.2s)/),
|
|
116
|
+
A_transition_color: has(probeA?.transition, /\bcolor\s+(?:200ms|0\.2s)/),
|
|
117
|
+
A_transition_border: has(probeA?.transition, /border-color\s+(?:200ms|0\.2s)/),
|
|
118
|
+
B_transition_opacity: has(probeB?.transition, /opacity\s+(?:200ms|0\.2s)/),
|
|
119
|
+
B_transition_bg: has(probeB?.transition, /background-color\s+(?:200ms|0\.2s)/),
|
|
120
|
+
};
|
|
121
|
+
const ok = Object.values(results).every(Boolean);
|
|
122
|
+
console.log(`${ok ? '✅' : '❌'} active-links empty recede:`, JSON.stringify(results),
|
|
123
|
+
`\n probeA (0 flows):`, probeA,
|
|
124
|
+
`\n probeB (3 flows):`, probeB);
|
|
125
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/* Round 161 verification: active-links chip bullet picks up
|
|
2
|
+
* the R160 freshness vocabulary.
|
|
3
|
+
*
|
|
4
|
+
* R10 fades canvas flow edges by freshness. R160 brought that
|
|
5
|
+
* vocabulary to the recent-signal panel rows via a 1.6-px cyan
|
|
6
|
+
* pip at row left edge. R161 extends it one more scope — up to
|
|
7
|
+
* the fleet-aggregate active-links chip in the chip-row.
|
|
8
|
+
*
|
|
9
|
+
* The chip text was already "3 active links · last 5s ago" but
|
|
10
|
+
* the " · " separator was dead gray. R161 colors that bullet
|
|
11
|
+
* by freshness using the same alpha ramp:
|
|
12
|
+
* ageSec ≤ 30 → 1.0 (fully fresh)
|
|
13
|
+
* 30-300s → 1.0 → 0.25 smooth decay
|
|
14
|
+
* > 300s → 0.25 stale floor
|
|
15
|
+
*
|
|
16
|
+
* Three nested scopes now share one freshness ladder:
|
|
17
|
+
* canvas edge fade (R10) — per-edge visual
|
|
18
|
+
* row pip (R160) — per-flow row in recent-signal
|
|
19
|
+
* chip bullet (R161) — fleet aggregate in chip-row
|
|
20
|
+
*
|
|
21
|
+
* Test scenarios (one page load each):
|
|
22
|
+
* A. fresh most-recent flow (5s ago) → alpha ≈ 1.0, fontWeight 700
|
|
23
|
+
* B. mid recency (90s ago) → alpha ≈ 0.83
|
|
24
|
+
* C. stale (360s ago) → alpha ≈ 0.25 (floor)
|
|
25
|
+
*
|
|
26
|
+
* Color parses out of `rgba(34, 211, 238, 1)` cyber theme.
|
|
27
|
+
*/
|
|
28
|
+
import { chromium } from 'playwright';
|
|
29
|
+
import { readFileSync } from 'node:fs';
|
|
30
|
+
|
|
31
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
32
|
+
const browser = await chromium.launch({ headless: true });
|
|
33
|
+
|
|
34
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
35
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
36
|
+
await ctx.addInitScript(() => {
|
|
37
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
async function probe(msgAgeSec) {
|
|
41
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
42
|
+
await ctx.unroute('**/api/hub/status*').catch(() => {});
|
|
43
|
+
await ctx.unroute('**/api/hub/messages*').catch(() => {});
|
|
44
|
+
await ctx.unroute('**/api/hub/tasks*').catch(() => {});
|
|
45
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
46
|
+
const r = await route.fetch();
|
|
47
|
+
const b = await r.json();
|
|
48
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
49
|
+
const mk = (alias) => ({
|
|
50
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
51
|
+
network_id: nid, project_dir: null,
|
|
52
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
53
|
+
});
|
|
54
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta')] } });
|
|
55
|
+
});
|
|
56
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
|
|
57
|
+
{ id: 'x', from_alias: 'alpha', to_alias: 'beta', content: 'hi',
|
|
58
|
+
network_id: 'default', created_at: new Date(Date.now() - msgAgeSec * 1000).toISOString() },
|
|
59
|
+
] } }));
|
|
60
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
61
|
+
|
|
62
|
+
const page = await ctx.newPage();
|
|
63
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
64
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 2, { timeout: 30000 });
|
|
65
|
+
await page.waitForSelector('[data-active-links-freshness-dot]', { timeout: 10000 });
|
|
66
|
+
await page.waitForTimeout(400);
|
|
67
|
+
const out = await page.evaluate(() => {
|
|
68
|
+
const el = document.querySelector('[data-active-links-freshness-dot]');
|
|
69
|
+
if (!el) return null;
|
|
70
|
+
const styleColor = el.style.color || getComputedStyle(el).color;
|
|
71
|
+
return {
|
|
72
|
+
alpha: parseFloat(el.getAttribute('data-active-links-freshness-alpha') || ''),
|
|
73
|
+
color: styleColor,
|
|
74
|
+
fontWeight: el.style.fontWeight || getComputedStyle(el).fontWeight,
|
|
75
|
+
text: el.textContent,
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
await page.close();
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const A = await probe(5); // fresh
|
|
83
|
+
const B = await probe(90); // mid
|
|
84
|
+
const C = await probe(360); // stale
|
|
85
|
+
|
|
86
|
+
await browser.close();
|
|
87
|
+
|
|
88
|
+
const cyanRgb = (str) => {
|
|
89
|
+
// Inline `style="color: rgba(34, 211, 238, ...)"` — alpha is part of color.
|
|
90
|
+
const m = /rgba?\((\d+),\s*(\d+),\s*(\d+)/.exec(str);
|
|
91
|
+
if (!m) return null;
|
|
92
|
+
return [Number(m[1]), Number(m[2]), Number(m[3])];
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const results = {
|
|
96
|
+
fresh_dot_found: A !== null,
|
|
97
|
+
mid_dot_found: B !== null,
|
|
98
|
+
stale_dot_found: C !== null,
|
|
99
|
+
fresh_alpha_1: A && Math.abs(A.alpha - 1.0) < 0.05,
|
|
100
|
+
mid_alpha_window: B && B.alpha > 0.7 && B.alpha < 0.95,
|
|
101
|
+
stale_alpha_floor: C && Math.abs(C.alpha - 0.25) < 0.05,
|
|
102
|
+
fresh_color_cyan: A && JSON.stringify(cyanRgb(A.color)) === '[34,211,238]',
|
|
103
|
+
fresh_weight_bold: A && (A.fontWeight === '700' || A.fontWeight === 'bold'),
|
|
104
|
+
stale_weight_norm: C && (C.fontWeight === '400' || C.fontWeight === 'normal' || C.fontWeight === ''),
|
|
105
|
+
ladder_descends: A && B && C && A.alpha > B.alpha && B.alpha > C.alpha,
|
|
106
|
+
text_is_separator: A && A.text && A.text.trim() === '·',
|
|
107
|
+
};
|
|
108
|
+
const ok = Object.values(results).every(Boolean);
|
|
109
|
+
console.log(`${ok ? '✅' : '❌'} active-links freshness:`, JSON.stringify(results),
|
|
110
|
+
`\n fresh =`, A,
|
|
111
|
+
`\n mid =`, B,
|
|
112
|
+
`\n stale =`, C);
|
|
113
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/* Round 114 verification: active-links chip tooltip lists the actual
|
|
2
|
+
* flow pairs. Closes the info-density sweep on the last chip-row
|
|
3
|
+
* hover surface (R97-R113 covered everything else). Format:
|
|
4
|
+
* "alpha→beta (3), gamma→delta (1) — hover brightens all"
|
|
5
|
+
*
|
|
6
|
+
* Drive 3 flow pairs of different counts. Verify each pair appears
|
|
7
|
+
* in the title with its count; the trailing action hint says
|
|
8
|
+
* "hover brightens all". Empty-flow fleet → no title.
|
|
9
|
+
*/
|
|
10
|
+
import { chromium } from 'playwright';
|
|
11
|
+
import { readFileSync } from 'node:fs';
|
|
12
|
+
|
|
13
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
14
|
+
const browser = await chromium.launch({ headless: true });
|
|
15
|
+
const fresh = new Date(Date.now() - 30 * 1000).toISOString();
|
|
16
|
+
|
|
17
|
+
const probe = async (withFlow) => {
|
|
18
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
19
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
20
|
+
await ctx.addInitScript(() => {
|
|
21
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
22
|
+
});
|
|
23
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
24
|
+
const r = await route.fetch();
|
|
25
|
+
const b = await r.json();
|
|
26
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
27
|
+
const mk = (alias) => ({
|
|
28
|
+
alias, status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
29
|
+
network_id: nid, project_dir: null,
|
|
30
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
31
|
+
});
|
|
32
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta'), mk('gamma'), mk('delta')] } });
|
|
33
|
+
});
|
|
34
|
+
const now = Date.now();
|
|
35
|
+
const mkMsg = (id, from_alias, to_alias, ageMs) => ({
|
|
36
|
+
id, from_alias, to_alias, content: 'hi', network_id: 'default',
|
|
37
|
+
created_at: new Date(now - ageMs).toISOString(),
|
|
38
|
+
});
|
|
39
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: withFlow ? [
|
|
40
|
+
mkMsg('m1', 'alpha', 'beta', 30 * 1000),
|
|
41
|
+
mkMsg('m2', 'alpha', 'beta', 40 * 1000),
|
|
42
|
+
mkMsg('m3', 'alpha', 'beta', 50 * 1000),
|
|
43
|
+
mkMsg('m4', 'gamma', 'delta', 60 * 1000),
|
|
44
|
+
] : [] } }));
|
|
45
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
46
|
+
const page = await ctx.newPage();
|
|
47
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
48
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
49
|
+
await page.waitForTimeout(600);
|
|
50
|
+
const data = await page.evaluate(() => {
|
|
51
|
+
const el = document.querySelector('[data-active-links-chip]');
|
|
52
|
+
return {
|
|
53
|
+
title: el?.getAttribute('title'),
|
|
54
|
+
flowCount: el?.getAttribute('data-active-links-flow-count') || '',
|
|
55
|
+
text: el?.textContent?.trim() || '',
|
|
56
|
+
};
|
|
57
|
+
});
|
|
58
|
+
await ctx.close();
|
|
59
|
+
return data;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const empty = await probe(false);
|
|
63
|
+
const full = await probe(true);
|
|
64
|
+
await browser.close();
|
|
65
|
+
|
|
66
|
+
const results = {
|
|
67
|
+
empty_noTitle: empty.title === null,
|
|
68
|
+
empty_zeroCount: empty.flowCount === '0',
|
|
69
|
+
full_titleHasAlphaBeta: /alpha→beta \(3\)/.test(full.title || ''),
|
|
70
|
+
full_titleHasGammaDelta: /gamma→delta \(1\)/.test(full.title || ''),
|
|
71
|
+
full_titleActionHint: /hover brightens all/.test(full.title || ''),
|
|
72
|
+
full_flowCount2: full.flowCount === '2',
|
|
73
|
+
full_chipShowsCount: /2 active link/.test(full.text),
|
|
74
|
+
};
|
|
75
|
+
const ok = Object.values(results).every(Boolean);
|
|
76
|
+
console.log(`${ok ? '✅' : '❌'} active-links tooltip:`, JSON.stringify(results),
|
|
77
|
+
`\n empty=`, empty,
|
|
78
|
+
`\n full=`, full);
|
|
79
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/* Round 243 verification: active-node pulse ring gets two polishes
|
|
2
|
+
* on a single element:
|
|
3
|
+
*
|
|
4
|
+
* 1. Always-mount-opacity-gate wrapper <g> — pulse stays in DOM
|
|
5
|
+
* for all online nodes (when reducedMotion is off); isActive
|
|
6
|
+
* flips wrapper opacity 1 ↔ 0 with 300ms ease-out, instead of
|
|
7
|
+
* conditional mount that snap-restarted SMIL from phase 0.
|
|
8
|
+
*
|
|
9
|
+
* 2. SMIL ease-in-out keySplines on both r + opacity animates —
|
|
10
|
+
* the breath rises and settles at both endpoints instead of
|
|
11
|
+
* linear constant-velocity interp.
|
|
12
|
+
*
|
|
13
|
+
* Scenario: 4 working agents + 5-msg alpha→beta flow.
|
|
14
|
+
* activeAliases = {alpha, beta} (active=true)
|
|
15
|
+
* gamma, delta = active=false
|
|
16
|
+
*
|
|
17
|
+
* Test scope per node:
|
|
18
|
+
* - [data-node-pulse] always present (4 pulses in DOM)
|
|
19
|
+
* - alpha + beta: data-node-pulse-active='true', wrapper opacity=1
|
|
20
|
+
* - gamma + delta: data-node-pulse-active='false', wrapper opacity=0
|
|
21
|
+
* - All 4 wrappers have style.transition with 'opacity 300ms'
|
|
22
|
+
* - All 4 contain two <animate> children (r + opacity)
|
|
23
|
+
* - Each <animate> has calcMode='spline', keyTimes='0;0.5;1',
|
|
24
|
+
* keySplines='0.42 0 0.58 1;0.42 0 0.58 1', dur='2.4s'
|
|
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 {
|
|
35
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
36
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
37
|
+
localStorage.setItem('anet-topo-layout', 'ring');
|
|
38
|
+
} catch {}
|
|
39
|
+
});
|
|
40
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
41
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
42
|
+
const r = await route.fetch();
|
|
43
|
+
const b = await r.json();
|
|
44
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
45
|
+
const mk = (alias) => ({
|
|
46
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
47
|
+
network_id: nid, project_dir: null,
|
|
48
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
49
|
+
});
|
|
50
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
51
|
+
mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
|
|
52
|
+
] } });
|
|
53
|
+
});
|
|
54
|
+
const now = Date.now();
|
|
55
|
+
const msgs = [];
|
|
56
|
+
for (let i = 0; i < 5; i++) {
|
|
57
|
+
msgs.push({
|
|
58
|
+
id: `m${i}`, from_alias: 'alpha', to_alias: 'beta', content: 'hi',
|
|
59
|
+
network_id: 'default', created_at: new Date(now - (1000 + i * 50)).toISOString(),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
await ctx.route('**/api/hub/messages*', (r2) => r2.fulfill({ json: { messages: msgs } }));
|
|
63
|
+
await ctx.route('**/api/hub/tasks*', (r2) => r2.fulfill({ json: { tasks: [] } }));
|
|
64
|
+
|
|
65
|
+
const page = await ctx.newPage();
|
|
66
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
67
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
68
|
+
await page.waitForSelector('[data-node-pulse]', { timeout: 10000, state: 'attached' });
|
|
69
|
+
await page.waitForTimeout(300);
|
|
70
|
+
|
|
71
|
+
const probe = await page.evaluate(() => {
|
|
72
|
+
const pulses = Array.from(document.querySelectorAll('[data-node-pulse]'));
|
|
73
|
+
return pulses.map((g) => {
|
|
74
|
+
const animates = Array.from(g.querySelectorAll('animate'));
|
|
75
|
+
return {
|
|
76
|
+
alias: g.getAttribute('data-node-pulse'),
|
|
77
|
+
active: g.getAttribute('data-node-pulse-active'),
|
|
78
|
+
opacityAttr: g.getAttribute('opacity'),
|
|
79
|
+
transition: g.style.transition,
|
|
80
|
+
animateCount: animates.length,
|
|
81
|
+
animates: animates.map(a => ({
|
|
82
|
+
attr: a.getAttribute('attributeName'),
|
|
83
|
+
calcMode: a.getAttribute('calcMode'),
|
|
84
|
+
keyTimes: a.getAttribute('keyTimes'),
|
|
85
|
+
keySplines: a.getAttribute('keySplines'),
|
|
86
|
+
dur: a.getAttribute('dur'),
|
|
87
|
+
})),
|
|
88
|
+
};
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
await browser.close();
|
|
92
|
+
|
|
93
|
+
const EASE_INOUT = '0.42 0 0.58 1;0.42 0 0.58 1';
|
|
94
|
+
const hasOpTransition = (s) => /opacity\s+(?:300ms|0\.3s)/.test(s || '');
|
|
95
|
+
const activeAliases = new Set(['alpha', 'beta']);
|
|
96
|
+
const expectAlias = (p) => activeAliases.has(p.alias) ? 'true' : 'false';
|
|
97
|
+
|
|
98
|
+
const animateOK = (a) =>
|
|
99
|
+
(a.attr === 'r' || a.attr === 'opacity') &&
|
|
100
|
+
a.calcMode === 'spline' &&
|
|
101
|
+
a.keyTimes === '0;0.5;1' &&
|
|
102
|
+
a.keySplines === EASE_INOUT &&
|
|
103
|
+
a.dur === '2.4s';
|
|
104
|
+
|
|
105
|
+
const results = {
|
|
106
|
+
four_pulses: probe.length === 4,
|
|
107
|
+
all_have_alias: probe.every(p => typeof p.alias === 'string'),
|
|
108
|
+
active_state_matches: probe.every(p => p.active === expectAlias(p)),
|
|
109
|
+
active_pulses_opacity_1: probe.filter(p => p.active === 'true').every(p => p.opacityAttr === '1'),
|
|
110
|
+
inactive_pulses_opacity_0: probe.filter(p => p.active === 'false').every(p => p.opacityAttr === '0'),
|
|
111
|
+
all_have_op_transition: probe.every(p => hasOpTransition(p.transition)),
|
|
112
|
+
all_two_animates: probe.every(p => p.animateCount === 2),
|
|
113
|
+
all_animates_have_r: probe.every(p => p.animates.some(a => a.attr === 'r')),
|
|
114
|
+
all_animates_have_opacity: probe.every(p => p.animates.some(a => a.attr === 'opacity')),
|
|
115
|
+
all_animates_ease_inout: probe.every(p => p.animates.every(animateOK)),
|
|
116
|
+
};
|
|
117
|
+
const ok = Object.values(results).every(Boolean);
|
|
118
|
+
console.log(`${ok ? '✅' : '❌'} active pulse ease+gate:`, JSON.stringify(results),
|
|
119
|
+
'\n pulses:', probe.map(p => ({ a: p.alias, active: p.active, op: p.opacityAttr })));
|
|
120
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/* Round 297 verification: brand-logo color transition.
|
|
2
|
+
*
|
|
3
|
+
* Codex's bbde2b7-era title-block sleep2agi crescent logo had
|
|
4
|
+
* theme-conditional color (cyber #67e8f9 ↔ light #0d9488) but no
|
|
5
|
+
* CSS transition — theme flips snapped the moon color in one
|
|
6
|
+
* frame. R297 adds `transition: color 200ms ease-out` so the
|
|
7
|
+
* brand mark joins the coordinated theme-toggle choreography
|
|
8
|
+
* (R245/R246/R247/R253/R254).
|
|
9
|
+
*
|
|
10
|
+
* Contract:
|
|
11
|
+
* - [data-topo-brand-logo] is present (regression — codex's
|
|
12
|
+
* integration shipped).
|
|
13
|
+
* - computed transition includes 'color' with ~200ms duration.
|
|
14
|
+
* - computed color matches cyber palette (#67e8f9 in cyber theme).
|
|
15
|
+
* - R296 kicker color stays gray-500 (regression).
|
|
16
|
+
* - R295 swatch r=6 + R294 pulse absent intact.
|
|
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, model) => ({
|
|
34
|
+
alias, status: 'working', model, 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', 'claude-opus-4'),
|
|
40
|
+
mk('beta', 'gpt-4o'),
|
|
41
|
+
] } });
|
|
42
|
+
});
|
|
43
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
44
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
45
|
+
|
|
46
|
+
const page = await ctx.newPage();
|
|
47
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
48
|
+
await page.waitForSelector('[data-topo-brand-logo]', { timeout: 15000 });
|
|
49
|
+
await page.waitForTimeout(300);
|
|
50
|
+
|
|
51
|
+
const probe = await page.evaluate(() => {
|
|
52
|
+
const logo = document.querySelector('[data-topo-brand-logo]');
|
|
53
|
+
if (!logo) return null;
|
|
54
|
+
const cs = getComputedStyle(logo);
|
|
55
|
+
const kicker = document.querySelector('[data-topo-section-kicker]');
|
|
56
|
+
const kCs = kicker ? getComputedStyle(kicker) : null;
|
|
57
|
+
const swatch = document.querySelector('[data-legend-swatch="working"]');
|
|
58
|
+
const pulses = document.querySelectorAll('[data-pulse-wrapper]');
|
|
59
|
+
return {
|
|
60
|
+
color: cs.color,
|
|
61
|
+
transition: cs.transition,
|
|
62
|
+
width: logo.getAttribute('width'),
|
|
63
|
+
kickerColor: kCs?.color ?? null,
|
|
64
|
+
swatchR: swatch?.getAttribute('r') ?? null,
|
|
65
|
+
pulseCount: pulses.length,
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
await browser.close();
|
|
69
|
+
|
|
70
|
+
const hasColorTransition = (s) => /color\s+0?\.?\d*s|color\s+\d+ms/i.test(s || '');
|
|
71
|
+
const isAround200ms = (s) => /(color\s+200ms|0\.2s)/i.test(s || '');
|
|
72
|
+
|
|
73
|
+
// gray-600 (#4b5563 = rgb(75,85,99))
|
|
74
|
+
const isNotGray600 = (s) => !/rgba?\(\s*75\s*,\s*85\s*,\s*99/.test(s || '');
|
|
75
|
+
// gray-500 (#6b7280 = rgb(107,114,128))
|
|
76
|
+
const isGrayish = (s) => /(rgb|oklab|lab|oklch|hsl)/.test(s || '');
|
|
77
|
+
|
|
78
|
+
const results = {
|
|
79
|
+
brand_logo_present: probe !== null,
|
|
80
|
+
// R316 bumped width 36 → 40; assertion updated.
|
|
81
|
+
brand_logo_width_40: probe?.width === '40',
|
|
82
|
+
transition_has_color: hasColorTransition(probe?.transition),
|
|
83
|
+
transition_200ms: isAround200ms(probe?.transition),
|
|
84
|
+
r296_kicker_not_gray600: isNotGray600(probe?.kickerColor),
|
|
85
|
+
r296_kicker_grayish: isGrayish(probe?.kickerColor),
|
|
86
|
+
r295_swatch_r_6: probe?.swatchR === '6',
|
|
87
|
+
r294_pulse_absent: probe?.pulseCount === 0,
|
|
88
|
+
};
|
|
89
|
+
const ok = Object.values(results).every(Boolean);
|
|
90
|
+
console.log(`${ok ? '✅' : '❌'} brand-logo transition:`, JSON.stringify(results),
|
|
91
|
+
'\n logo color:', probe?.color,
|
|
92
|
+
'\n logo transition:', probe?.transition,
|
|
93
|
+
'\n kicker color:', probe?.kickerColor);
|
|
94
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/* Round 282 verification: sleep2agi brand watermark at canvas bottom-left.
|
|
2
|
+
*
|
|
3
|
+
* Per Vincent 5215 ask via 通信龙 (logo placement on the canvas), R282
|
|
4
|
+
* adds a low-opacity monospace text "sleep2agi" at viewBox (16, 672)
|
|
5
|
+
* — bottom-left corner, the only fully-empty corner after R275-R281
|
|
6
|
+
* 减法 arc:
|
|
7
|
+
* top-left: recent-signal panel
|
|
8
|
+
* top-right: legend panel
|
|
9
|
+
* bottom-right: chrome strip (HTML overlay)
|
|
10
|
+
* bottom-left: EMPTY → watermark home
|
|
11
|
+
*
|
|
12
|
+
* No icon yet (no sleep2agi-specific crescent asset in public/);
|
|
13
|
+
* text-only watermark is the minimal-risk implementation. R283+
|
|
14
|
+
* can swap in a real logo if Vincent provides asset.
|
|
15
|
+
*
|
|
16
|
+
* Test scope:
|
|
17
|
+
* 1. data-topo-brand-watermark element exists.
|
|
18
|
+
* 2. Text content === "sleep2agi".
|
|
19
|
+
* 3. x === 16, y === 672 (bottom-left, 16-unit inset matching panels).
|
|
20
|
+
* 4. opacity === "0.4" (subtle watermark).
|
|
21
|
+
* 5. fill is set + theme-aware (not 'none', not empty).
|
|
22
|
+
* 6. R281 vendor chip threshold regression: 2-vendor scenario chip absent.
|
|
23
|
+
* 7. R280 spokes still retired (regression).
|
|
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
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
31
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
32
|
+
await ctx.addInitScript(() => {
|
|
33
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
34
|
+
});
|
|
35
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
36
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
37
|
+
const r = await route.fetch();
|
|
38
|
+
const b = await r.json();
|
|
39
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
40
|
+
// 2-vendor scenario (claude + gpt) so R281 vendor chip should be absent
|
|
41
|
+
const mk = (alias, model) => ({
|
|
42
|
+
alias, status: 'working', model, 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', 'claude-opus-4'),
|
|
48
|
+
mk('beta', 'gpt-4o'),
|
|
49
|
+
mk('gamma', 'claude-sonnet-4'),
|
|
50
|
+
mk('delta', 'gpt-4'),
|
|
51
|
+
] } });
|
|
52
|
+
});
|
|
53
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
54
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
55
|
+
|
|
56
|
+
const page = await ctx.newPage();
|
|
57
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
58
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
59
|
+
await page.waitForSelector('[data-topo-brand-watermark]', { timeout: 10000 });
|
|
60
|
+
await page.waitForTimeout(300);
|
|
61
|
+
|
|
62
|
+
const probe = await page.evaluate(() => {
|
|
63
|
+
const mark = document.querySelector('[data-topo-brand-watermark]');
|
|
64
|
+
const vendorChips = document.querySelectorAll('[data-vendor-letter]');
|
|
65
|
+
const spokes = document.querySelectorAll('[data-topo-spoke-angle]');
|
|
66
|
+
return {
|
|
67
|
+
present: mark !== null,
|
|
68
|
+
text: mark?.textContent ?? null,
|
|
69
|
+
x: mark ? +mark.getAttribute('x') : null,
|
|
70
|
+
y: mark ? +mark.getAttribute('y') : null,
|
|
71
|
+
opacity: mark?.getAttribute('opacity') ?? null,
|
|
72
|
+
fill: mark ? window.getComputedStyle(mark).fill : null,
|
|
73
|
+
vendorChipCount: vendorChips.length,
|
|
74
|
+
spokeCount: spokes.length,
|
|
75
|
+
};
|
|
76
|
+
});
|
|
77
|
+
await browser.close();
|
|
78
|
+
|
|
79
|
+
const results = {
|
|
80
|
+
watermark_present: probe.present,
|
|
81
|
+
watermark_text_is_brand: probe.text === 'sleep2agi',
|
|
82
|
+
watermark_x_is_16: probe.x === 16,
|
|
83
|
+
watermark_y_is_672: probe.y === 672,
|
|
84
|
+
watermark_opacity_0_4: probe.opacity === '0.4',
|
|
85
|
+
watermark_fill_set: probe.fill != null && probe.fill !== '' && probe.fill !== 'none',
|
|
86
|
+
r281_vendor_chip_absent_in_2_vendor: probe.vendorChipCount === 0,
|
|
87
|
+
r280_spokes_absent: probe.spokeCount === 0,
|
|
88
|
+
};
|
|
89
|
+
const ok = Object.values(results).every(Boolean);
|
|
90
|
+
console.log(`${ok ? '✅' : '❌'} brand watermark:`, JSON.stringify(results),
|
|
91
|
+
'\n text:', JSON.stringify(probe.text),
|
|
92
|
+
'\n (x,y):', `(${probe.x}, ${probe.y})`,
|
|
93
|
+
'\n opacity:', probe.opacity,
|
|
94
|
+
'\n fill:', probe.fill,
|
|
95
|
+
'\n vendor chips (expect 0):', probe.vendorChipCount,
|
|
96
|
+
'\n spokes (expect 0):', probe.spokeCount);
|
|
97
|
+
process.exit(ok ? 0 : 1);
|