@sleep2agi/agent-network-dashboard 0.4.6-preview.31 → 0.4.6-preview.310
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/build-manifest.json +3 -3
- package/.next/diagnostics/route-bundle-stats.json +41 -41
- package/.next/fallback-build-manifest.json +3 -3
- package/.next/prerender-manifest.json +3 -3
- 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/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/chunks/ssr/[root-of-the-server]__03b.f76._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +4 -1
- 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_057q.ne._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.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_0_d45-d._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.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/0-mpa_947ipeq.js +1 -0
- package/.next/static/chunks/01lmbsd37fybu.js +1 -0
- package/.next/static/chunks/03o.h6kvmw4l_.js +1 -0
- package/.next/static/chunks/03~~oirxz7~vc.js +1 -0
- package/.next/static/chunks/05uk96gc~9mni.js +1 -0
- package/.next/static/chunks/08kf-.bgs10ze.js +1 -0
- package/.next/static/chunks/0ggj5ummw2a84.js +7 -0
- package/.next/static/chunks/0glmrdj9112tc.js +7 -0
- package/.next/static/chunks/0o40uf-rn28q6.js +1 -0
- package/.next/static/chunks/0q3hr1~viwzkt.js +4 -0
- package/.next/static/chunks/0rxrez_cjn.eh.js +1 -0
- package/.next/static/chunks/0x8x.29hkju5a.js +1 -0
- package/.next/static/chunks/0~ks9b48lw8hx.css +2 -0
- package/.next/static/chunks/13lvf-h-j8bmc.js +1 -0
- package/.next/static/chunks/174re5ar~kxne.js +1 -0
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/app/components/ChatPopover.tsx +38 -2
- package/app/components/CommandPalette.tsx +107 -0
- package/app/components/HelpOverlay.tsx +126 -0
- package/app/components/TopoGraph.tsx +5927 -258
- package/app/components/utils.ts +8 -7
- package/app/globals.css +110 -5
- package/app/lib/time.ts +36 -0
- package/app/lib/vendorIdentity.ts +14 -3
- 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/scripts/help-overlay-topo-test.mjs +44 -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-chip-test.mjs +73 -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-auto-fit-test.mjs +73 -0
- package/scripts/topo-brand-watermark-test.mjs +97 -0
- package/scripts/topo-canvas-aria-test.mjs +108 -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-semantic-gap-test.mjs +97 -0
- package/scripts/topo-chip-row-tabular-test.mjs +111 -0
- package/scripts/topo-chrome-a11y-test.mjs +107 -0
- package/scripts/topo-chrome-icon-stroke-test.mjs +95 -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-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-cwd-tooltip-test.mjs +64 -0
- package/scripts/topo-dblclick-reset-test.mjs +74 -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 +93 -0
- package/scripts/topo-edge-pill-hot-test.mjs +129 -0
- package/scripts/topo-edge-pill-test.mjs +105 -0
- package/scripts/topo-edge-ping-pulse-retired-test.mjs +119 -0
- package/scripts/topo-edge-stroke-ease-test.mjs +100 -0
- package/scripts/topo-edge-stroke-transition-test.mjs +132 -0
- package/scripts/topo-edge-tooltip-test.mjs +70 -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-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-test.mjs +110 -0
- package/scripts/topo-filter-pill-mobile-test.mjs +90 -0
- package/scripts/topo-fit-key-test.mjs +106 -0
- package/scripts/topo-freshness-chip-test.mjs +74 -0
- package/scripts/topo-freshness-chip-transition-test.mjs +80 -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 +100 -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-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 +93 -0
- 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-label-card-theme-ease-test.mjs +85 -0
- package/scripts/topo-label-lift-test.mjs +119 -0
- package/scripts/topo-lastseen-tooltip-test.mjs +59 -0
- package/scripts/topo-layout-hover-text-preview-test.mjs +101 -0
- package/scripts/topo-layout-key-test.mjs +91 -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-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-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-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-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-rect-stroke-test.mjs +94 -0
- package/scripts/topo-minimap-test.mjs +68 -0
- package/scripts/topo-minimap-viewport-smooth-test.mjs +124 -0
- package/scripts/topo-mobile-chips-test.mjs +103 -0
- package/scripts/topo-monogram-font-test.mjs +112 -0
- package/scripts/topo-monogram-stroke-test.mjs +101 -0
- package/scripts/topo-node-a11y-test.mjs +107 -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-overlap-test.mjs +32 -5
- 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-theme-ease-test.mjs +85 -0
- package/scripts/topo-panel-title-fill-ease-test.mjs +91 -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-popover-meta-test.mjs +78 -0
- package/scripts/topo-prepublish-guard-test.mjs +95 -0
- package/scripts/topo-pressure-bar-test.mjs +69 -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-pulse-rate-test.mjs +71 -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-footer-breath-test.mjs +142 -0
- package/scripts/topo-recent-panel-hot-header-test.mjs +132 -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-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-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-recent-signal-empty-test.mjs +63 -0
- package/scripts/topo-reduced-motion-test.mjs +77 -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-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-toggle-inactive-cyan-hover-test.mjs +110 -0
- package/scripts/topo-top-rail-theme-ease-test.mjs +89 -0
- package/scripts/topo-tz-ghost-test.mjs +69 -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-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-shadow-theme-ease-test.mjs +89 -0
- package/.next/static/chunks/0-7iowzu4.798.js +0 -1
- package/.next/static/chunks/01u5zo8b7r_r3.js +0 -1
- package/.next/static/chunks/02t5jr4shh.hy.js +0 -1
- package/.next/static/chunks/09nd.t_hd.y6t.css +0 -2
- package/.next/static/chunks/0_qfg5s5uzs7..js +0 -7
- package/.next/static/chunks/0elalb9n_.~di.js +0 -1
- package/.next/static/chunks/0fqit.b4j_6rz.js +0 -1
- package/.next/static/chunks/0j_q__o8p6g83.js +0 -1
- package/.next/static/chunks/0jl6_w50zh4l5.js +0 -1
- package/.next/static/chunks/0pl7l_.2im.j0.js +0 -7
- package/.next/static/chunks/0qcfcgdthhos6.js +0 -1
- package/.next/static/chunks/0r4q73.-1bkyl.js +0 -1
- package/.next/static/chunks/0r_c8eccg3z2o.js +0 -1
- package/.next/static/chunks/10oi7l-tycgl9.js +0 -1
- package/.next/static/chunks/11uk1izuv3~0t.js +0 -1
- /package/.next/static/{mgs4E0Fu2JlZsA5DB0XUu → Kkkh4U0z1GCAKfQMsfmjt}/_buildManifest.js +0 -0
- /package/.next/static/{mgs4E0Fu2JlZsA5DB0XUu → Kkkh4U0z1GCAKfQMsfmjt}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{mgs4E0Fu2JlZsA5DB0XUu → Kkkh4U0z1GCAKfQMsfmjt}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/* Round 163 verification: Ring/Grid layout toggle buttons pick
|
|
2
|
+
* up the R154 chrome-button polish convention.
|
|
3
|
+
*
|
|
4
|
+
* Pre-R163 the buttons had aria-pressed + transition-colors but:
|
|
5
|
+
* - no focus-visible ring (browser default invisible on canvas)
|
|
6
|
+
* - inactive hover only nudged text-gray-500 → gray-400, no bg
|
|
7
|
+
*
|
|
8
|
+
* R163 adds:
|
|
9
|
+
* data-topo-chrome-layout="ring|grid"
|
|
10
|
+
* data-topo-chrome-layout-active="true|false"
|
|
11
|
+
* focus-visible:ring-2 ring-cyan-400/60 ring-inset
|
|
12
|
+
* inactive: hover:text-gray-300 + hover:bg-cyan-500/5
|
|
13
|
+
* active: hover:bg-cyan-500/20 (slightly hotter)
|
|
14
|
+
*
|
|
15
|
+
* Test:
|
|
16
|
+
* 1. Both buttons present with data-topo-chrome-layout attr
|
|
17
|
+
* 2. Active button has bg-cyan-500/15 class fragment (existing)
|
|
18
|
+
* 3. Focus the inactive button → focus-visible ring class fragment
|
|
19
|
+
* 4. Hover the inactive button → bg color shifts from transparent
|
|
20
|
+
* to a cyan-ish tint (border bg-cyan-500/5)
|
|
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
|
+
const browser = await chromium.launch({ headless: true });
|
|
27
|
+
|
|
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'); localStorage.setItem('anet-topo-layout', 'ring'); } catch {}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
35
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
36
|
+
const r = await route.fetch();
|
|
37
|
+
const b = await r.json();
|
|
38
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
39
|
+
const mk = (alias) => ({
|
|
40
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
41
|
+
network_id: nid, project_dir: null,
|
|
42
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
43
|
+
});
|
|
44
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta')] } });
|
|
45
|
+
});
|
|
46
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
47
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
48
|
+
|
|
49
|
+
const page = await ctx.newPage();
|
|
50
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
51
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 2, { timeout: 30000 });
|
|
52
|
+
await page.waitForSelector('[data-topo-chrome-layout]', { timeout: 10000 });
|
|
53
|
+
await page.waitForTimeout(400);
|
|
54
|
+
|
|
55
|
+
// Probe basic attrs & class membership
|
|
56
|
+
const probe = await page.evaluate(() => {
|
|
57
|
+
const ring = document.querySelector('[data-topo-chrome-layout="ring"]');
|
|
58
|
+
const grid = document.querySelector('[data-topo-chrome-layout="grid"]');
|
|
59
|
+
if (!ring || !grid) return null;
|
|
60
|
+
return {
|
|
61
|
+
ring_active: ring.getAttribute('data-topo-chrome-layout-active'),
|
|
62
|
+
grid_active: grid.getAttribute('data-topo-chrome-layout-active'),
|
|
63
|
+
ring_pressed: ring.getAttribute('aria-pressed'),
|
|
64
|
+
grid_pressed: grid.getAttribute('aria-pressed'),
|
|
65
|
+
ring_class: ring.getAttribute('class') || '',
|
|
66
|
+
grid_class: grid.getAttribute('class') || '',
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Focus the inactive (grid) button — focus-visible ring should kick in.
|
|
71
|
+
// Tab-focus produces the :focus-visible state; .focus() doesn't always.
|
|
72
|
+
// Use page.keyboard.press('Tab') after first focusing somewhere known.
|
|
73
|
+
await page.locator('[data-topo-chrome-layout="grid"]').focus();
|
|
74
|
+
// trigger keyboard navigation to enable focus-visible state heuristic
|
|
75
|
+
await page.evaluate(() => {
|
|
76
|
+
const el = document.querySelector('[data-topo-chrome-layout="grid"]');
|
|
77
|
+
if (el) el.matches(':focus-visible');
|
|
78
|
+
});
|
|
79
|
+
const focusVisibleProbe = await page.evaluate(() => {
|
|
80
|
+
const el = document.querySelector('[data-topo-chrome-layout="grid"]');
|
|
81
|
+
if (!el) return null;
|
|
82
|
+
return {
|
|
83
|
+
isActiveElement: document.activeElement === el,
|
|
84
|
+
// Inspect outline & box-shadow because the Tailwind focus-visible:ring
|
|
85
|
+
// class compiles to a box-shadow. After .focus() Chromium reports
|
|
86
|
+
// :focus-visible heuristically — we verify the class fragment exists
|
|
87
|
+
// in the stylesheet bound to this element rather than relying on the
|
|
88
|
+
// dynamic pseudo-class match.
|
|
89
|
+
classFragments: {
|
|
90
|
+
hasFocusVisibleRingClass: (el.getAttribute('class') || '').includes('focus-visible:ring-cyan-400/60'),
|
|
91
|
+
hasRingInset: (el.getAttribute('class') || '').includes('focus-visible:ring-inset'),
|
|
92
|
+
hasFocusOutlineNone: (el.getAttribute('class') || '').includes('focus:outline-none'),
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Hover the inactive (grid) button — bg should shift away from transparent.
|
|
98
|
+
const gridBgBefore = await page.evaluate(() => {
|
|
99
|
+
const el = document.querySelector('[data-topo-chrome-layout="grid"]');
|
|
100
|
+
return el ? getComputedStyle(el).backgroundColor : null;
|
|
101
|
+
});
|
|
102
|
+
await page.locator('[data-topo-chrome-layout="grid"]').hover();
|
|
103
|
+
await page.waitForTimeout(300);
|
|
104
|
+
const gridBgHover = await page.evaluate(() => {
|
|
105
|
+
const el = document.querySelector('[data-topo-chrome-layout="grid"]');
|
|
106
|
+
return el ? getComputedStyle(el).backgroundColor : null;
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
await browser.close();
|
|
110
|
+
|
|
111
|
+
const results = {
|
|
112
|
+
both_buttons_found: probe !== null,
|
|
113
|
+
ring_is_active: probe?.ring_active === 'true',
|
|
114
|
+
grid_is_inactive: probe?.grid_active === 'false',
|
|
115
|
+
ring_aria_pressed: probe?.ring_pressed === 'true',
|
|
116
|
+
grid_aria_unpressed: probe?.grid_pressed === 'false',
|
|
117
|
+
active_has_cyan_bg_class: probe?.ring_class.includes('bg-cyan-500/15'),
|
|
118
|
+
inactive_hover_bg_class: probe?.grid_class.includes('hover:bg-cyan-500/5'),
|
|
119
|
+
active_hover_bg_class: probe?.ring_class.includes('hover:bg-cyan-500/20'),
|
|
120
|
+
focus_outline_none: focusVisibleProbe?.classFragments?.hasFocusOutlineNone === true,
|
|
121
|
+
focus_visible_ring_class: focusVisibleProbe?.classFragments?.hasFocusVisibleRingClass === true,
|
|
122
|
+
focus_visible_ring_inset: focusVisibleProbe?.classFragments?.hasRingInset === true,
|
|
123
|
+
inactive_bg_before: gridBgBefore !== null,
|
|
124
|
+
hover_changes_bg: gridBgBefore !== gridBgHover,
|
|
125
|
+
};
|
|
126
|
+
const ok = Object.values(results).every(Boolean);
|
|
127
|
+
console.log(`${ok ? '✅' : '❌'} layout toggle polish:`, JSON.stringify(results),
|
|
128
|
+
`\n probe=`, probe,
|
|
129
|
+
`\n focus=`, focusVisibleProbe,
|
|
130
|
+
`\n bg before/hover=`, gridBgBefore, '→', gridBgHover);
|
|
131
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/* Round 277 verification: legend panel compress 104 → 88, matching the
|
|
2
|
+
* recent-signal panel's height post-R256. Row stride 24 → 18.
|
|
3
|
+
*
|
|
4
|
+
* Pre-R277:
|
|
5
|
+
* Panel: 224×104, rx=8
|
|
6
|
+
* Row 1 (working): y0=32, y1=36
|
|
7
|
+
* Row 2 (idle): y0=56, y1=60
|
|
8
|
+
* Row 3 (offline): y0=80, y1=84
|
|
9
|
+
* Flow-arrow: M140,80 Q164,56 196,80 (endpoints y=80)
|
|
10
|
+
*
|
|
11
|
+
* Post-R277:
|
|
12
|
+
* Panel: 224×88, rx=8
|
|
13
|
+
* Row 1 (working): y0=32, y1=36 (anchored — R271 hitbox y=21 invariant)
|
|
14
|
+
* Row 2 (idle): y0=50, y1=54 (-6)
|
|
15
|
+
* Row 3 (offline): y0=68, y1=72 (-12)
|
|
16
|
+
* Flow-arrow: M140,68 Q164,44 196,68 (tracks new offline cy)
|
|
17
|
+
*
|
|
18
|
+
* Net: legend panel takes ~15% less vertical chrome; panel pair
|
|
19
|
+
* (recent-signal 88 + legend 88) now share the same height. Third
|
|
20
|
+
* 减法 cut after R275 (FreshnessChip conditional) + R276 (orbit
|
|
21
|
+
* particles retired).
|
|
22
|
+
*
|
|
23
|
+
* Test scope:
|
|
24
|
+
* 1. Legend panel rect height === 88.
|
|
25
|
+
* 2. Recent-signal panel rect height === 88 (regression — R256 invariant).
|
|
26
|
+
* 3. Working row hitbox y === 21 (R271 invariant: row.y0=32 unchanged).
|
|
27
|
+
* 4. Flow-arrow path starts with "M140,68" (tracks new offline cy=68).
|
|
28
|
+
* 5. R275 freshness chip absent at fresh (regression).
|
|
29
|
+
* 6. R276 orbit particles absent (regression).
|
|
30
|
+
*/
|
|
31
|
+
import { chromium } from 'playwright';
|
|
32
|
+
import { readFileSync } from 'node:fs';
|
|
33
|
+
|
|
34
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
35
|
+
const browser = await chromium.launch({ headless: true });
|
|
36
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
37
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
38
|
+
await ctx.addInitScript(() => {
|
|
39
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
40
|
+
});
|
|
41
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
42
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
43
|
+
const r = await route.fetch();
|
|
44
|
+
const b = await r.json();
|
|
45
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
46
|
+
const mk = (alias) => ({
|
|
47
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
48
|
+
network_id: nid, project_dir: null,
|
|
49
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
50
|
+
});
|
|
51
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
52
|
+
mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
|
|
53
|
+
] } });
|
|
54
|
+
});
|
|
55
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
56
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
57
|
+
|
|
58
|
+
const page = await ctx.newPage();
|
|
59
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
60
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
61
|
+
await page.waitForSelector('[data-topo-panel="legend"]', { timeout: 10000 });
|
|
62
|
+
await page.waitForSelector('[data-topo-panel="recent"]', { timeout: 10000 });
|
|
63
|
+
await page.waitForSelector('[data-legend-status="working"] rect', { timeout: 10000 });
|
|
64
|
+
await page.waitForSelector('[data-legend-flow-arrow]', { timeout: 10000 });
|
|
65
|
+
await page.waitForTimeout(300);
|
|
66
|
+
|
|
67
|
+
// Hover working row to materialize the tinted hitbox rect for the
|
|
68
|
+
// y=21 invariant check (R271). Without hover the rect carries the
|
|
69
|
+
// same y attr but tint fills are transparent.
|
|
70
|
+
await page.locator('[data-legend-status="working"]').hover();
|
|
71
|
+
await page.waitForTimeout(150);
|
|
72
|
+
|
|
73
|
+
const probe = await page.evaluate(() => {
|
|
74
|
+
const legendG = document.querySelector('[data-topo-panel="legend"]');
|
|
75
|
+
const recentG = document.querySelector('[data-topo-panel="recent"]');
|
|
76
|
+
const workingRow = document.querySelector('[data-legend-status="working"]');
|
|
77
|
+
const workingHitbox = workingRow ? workingRow.querySelector('rect') : null;
|
|
78
|
+
const flowArrow = document.querySelector('[data-legend-flow-arrow]');
|
|
79
|
+
const freshnessChip = document.querySelector('[data-freshness-chip]');
|
|
80
|
+
const orbits = document.querySelectorAll('[data-topo-orbit-bucket]');
|
|
81
|
+
return {
|
|
82
|
+
legendHeight: legendG?.querySelector('rect')?.getAttribute('height') ?? null,
|
|
83
|
+
recentHeight: recentG?.querySelector('rect')?.getAttribute('height') ?? null,
|
|
84
|
+
workingHitboxY: workingHitbox ? +workingHitbox.getAttribute('y') : null,
|
|
85
|
+
flowArrowD: flowArrow ? flowArrow.getAttribute('d') : null,
|
|
86
|
+
freshnessPresent: freshnessChip !== null,
|
|
87
|
+
orbitCount: orbits.length,
|
|
88
|
+
};
|
|
89
|
+
});
|
|
90
|
+
await browser.close();
|
|
91
|
+
|
|
92
|
+
const results = {
|
|
93
|
+
legend_height_88: probe.legendHeight === '88',
|
|
94
|
+
recent_height_88_regression: probe.recentHeight === '88',
|
|
95
|
+
r271_working_hitbox_y_21: probe.workingHitboxY === 21,
|
|
96
|
+
flow_arrow_starts_at_y68: probe.flowArrowD === 'M140,68 Q164,44 196,68',
|
|
97
|
+
r275_freshness_absent: probe.freshnessPresent === false,
|
|
98
|
+
r276_orbit_absent: probe.orbitCount === 0,
|
|
99
|
+
};
|
|
100
|
+
const ok = Object.values(results).every(Boolean);
|
|
101
|
+
console.log(`${ok ? '✅' : '❌'} legend compress:`, JSON.stringify(results),
|
|
102
|
+
'\n legend height:', probe.legendHeight,
|
|
103
|
+
'\n recent height:', probe.recentHeight,
|
|
104
|
+
'\n working hitbox y:', probe.workingHitboxY,
|
|
105
|
+
'\n flow arrow d:', probe.flowArrowD,
|
|
106
|
+
'\n orbit count:', probe.orbitCount);
|
|
107
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/* Round 204 verification: legend count text recedes when its status
|
|
2
|
+
* tier is empty (count = 0). Pre-R204 "0" at opacity 0.65 read
|
|
3
|
+
* visually identical to "12" at opacity 0.65; R204 drops empty rows
|
|
4
|
+
* to 0.30 (dark) / 0.28 (light) so the eye gets a "no nodes in this
|
|
5
|
+
* tier" signal without parsing the digit.
|
|
6
|
+
*
|
|
7
|
+
* Test (cyber dark, all 4 sessions = working → idle=0, offline=0):
|
|
8
|
+
* 1. Three legend count texts present with data-legend-count for
|
|
9
|
+
* working / idle / offline.
|
|
10
|
+
* 2. Working row: count='4', data-legend-count-empty='false',
|
|
11
|
+
* opacity = 0.65 (idle, not hovered).
|
|
12
|
+
* 3. Idle row: count='0', data-legend-count-empty='true',
|
|
13
|
+
* opacity ≈ 0.30 (dim recede).
|
|
14
|
+
* 4. Offline row: count='0', data-legend-count-empty='true',
|
|
15
|
+
* opacity ≈ 0.30.
|
|
16
|
+
* 5. Hover working row → its count bumps to opacity ~0.95
|
|
17
|
+
* (existing R95 hover behaviour unbroken).
|
|
18
|
+
* 6. Hover idle row → empty-tier opacity DOES NOT bump (empty
|
|
19
|
+
* gate wins over hover). Recedes regardless.
|
|
20
|
+
* 7. Transition: opacity 150ms ease-out still on all three.
|
|
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
|
+
const browser = await chromium.launch({ headless: true });
|
|
27
|
+
|
|
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
|
+
|
|
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
|
+
// All 4 working — idle / offline tiers empty
|
|
40
|
+
const mk = (alias) => ({
|
|
41
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
42
|
+
network_id: nid, project_dir: null,
|
|
43
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
44
|
+
});
|
|
45
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
46
|
+
mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
|
|
47
|
+
] } });
|
|
48
|
+
});
|
|
49
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
50
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
51
|
+
|
|
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.waitForSelector('[data-legend-count="working"]', { timeout: 10000 });
|
|
56
|
+
await page.waitForTimeout(300);
|
|
57
|
+
|
|
58
|
+
const probe = async () => page.evaluate(() => {
|
|
59
|
+
const pick = (key) => {
|
|
60
|
+
const el = document.querySelector(`[data-legend-count="${key}"]`);
|
|
61
|
+
if (!el) return null;
|
|
62
|
+
return {
|
|
63
|
+
key,
|
|
64
|
+
text: el.textContent,
|
|
65
|
+
empty: el.getAttribute('data-legend-count-empty'),
|
|
66
|
+
opacity: parseFloat(el.getAttribute('opacity') || ''),
|
|
67
|
+
transition: el.style.transition,
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
return { working: pick('working'), idle: pick('idle'), offline: pick('offline') };
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const idle = await probe();
|
|
74
|
+
|
|
75
|
+
// Hover the working row — verify R95 hover bump still fires (count > 0)
|
|
76
|
+
await page.hover('[data-legend-status="working"]');
|
|
77
|
+
await page.waitForTimeout(200);
|
|
78
|
+
const hovWorking = await probe();
|
|
79
|
+
|
|
80
|
+
// Move off, then hover the empty (idle=0) row — verify empty gate wins
|
|
81
|
+
await page.mouse.move(0, 0);
|
|
82
|
+
await page.waitForTimeout(200);
|
|
83
|
+
await page.hover('[data-legend-status="idle"]');
|
|
84
|
+
await page.waitForTimeout(200);
|
|
85
|
+
const hovIdle = await probe();
|
|
86
|
+
|
|
87
|
+
await page.mouse.move(0, 0);
|
|
88
|
+
await page.waitForTimeout(200);
|
|
89
|
+
|
|
90
|
+
await browser.close();
|
|
91
|
+
|
|
92
|
+
const opacityNear = (got, want, tol = 0.04) => Math.abs(got - want) < tol;
|
|
93
|
+
|
|
94
|
+
const results = {
|
|
95
|
+
working_present: !!idle?.working,
|
|
96
|
+
idle_present: !!idle?.idle,
|
|
97
|
+
offline_present: !!idle?.offline,
|
|
98
|
+
|
|
99
|
+
working_text_4: idle?.working?.text === '4',
|
|
100
|
+
working_not_empty: idle?.working?.empty === 'false',
|
|
101
|
+
working_opacity_0p65: opacityNear(idle?.working?.opacity ?? 0, 0.65),
|
|
102
|
+
|
|
103
|
+
idle_text_0: idle?.idle?.text === '0',
|
|
104
|
+
idle_empty_true: idle?.idle?.empty === 'true',
|
|
105
|
+
idle_opacity_dim: opacityNear(idle?.idle?.opacity ?? 0, 0.30),
|
|
106
|
+
|
|
107
|
+
offline_text_0: idle?.offline?.text === '0',
|
|
108
|
+
offline_empty_true: idle?.offline?.empty === 'true',
|
|
109
|
+
offline_opacity_dim: opacityNear(idle?.offline?.opacity ?? 0, 0.30),
|
|
110
|
+
|
|
111
|
+
// R95 hover still bumps when row populated
|
|
112
|
+
working_hover_opacity_high: opacityNear(hovWorking?.working?.opacity ?? 0, 0.95),
|
|
113
|
+
|
|
114
|
+
// Empty-gate wins: hovering an empty row does NOT bump its count opacity
|
|
115
|
+
idle_hover_stays_dim: opacityNear(hovIdle?.idle?.opacity ?? 0, 0.30),
|
|
116
|
+
|
|
117
|
+
// Transition still in place across all 3
|
|
118
|
+
all_have_transition: ['working', 'idle', 'offline'].every(k =>
|
|
119
|
+
/opacity\s+(?:150ms|0\.15s)/.test(idle?.[k]?.transition || '')),
|
|
120
|
+
};
|
|
121
|
+
const ok = Object.values(results).every(Boolean);
|
|
122
|
+
console.log(`${ok ? '✅' : '❌'} legend count empty:`, JSON.stringify(results),
|
|
123
|
+
`\n idle:`, idle,
|
|
124
|
+
`\n hovWorking:`, hovWorking,
|
|
125
|
+
`\n hovIdle:`, hovIdle);
|
|
126
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/* Round 292 verification: legend panel header count picks up explicit
|
|
2
|
+
* fontVariantNumeric='tabular-nums' parity with the recent-signal
|
|
3
|
+
* panel header count (R232). Both panel headers now declare tabular-
|
|
4
|
+
* nums explicitly in inline style.
|
|
5
|
+
*
|
|
6
|
+
* Pre-R292 the legend count inherited tabular digit width via
|
|
7
|
+
* monospace but lacked the explicit directive its sibling
|
|
8
|
+
* recent-signal count carries. Post-R292 the panel-header pair
|
|
9
|
+
* shares the same explicit declaration.
|
|
10
|
+
*
|
|
11
|
+
* Note distinct from R274 (topo-legend-count-tabular-test.mjs)
|
|
12
|
+
* which probes the per-ROW counts (working/idle/offline). R292
|
|
13
|
+
* is the panel-level HEADER count ("N nodes" right-anchored).
|
|
14
|
+
*
|
|
15
|
+
* Contract:
|
|
16
|
+
* - [data-legend-panel-count] computed font-variant-numeric
|
|
17
|
+
* includes 'tabular-nums'.
|
|
18
|
+
* - Text content still '{N} node[s]'.
|
|
19
|
+
* - [data-recent-panel-count] also tabular (sibling parity check).
|
|
20
|
+
* - R291 starfield 14 dots + R290 three radar rings intact.
|
|
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
|
+
const browser = await chromium.launch({ headless: true });
|
|
27
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
28
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
29
|
+
await ctx.addInitScript(() => {
|
|
30
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
31
|
+
});
|
|
32
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
33
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
34
|
+
const r = await route.fetch();
|
|
35
|
+
const b = await r.json();
|
|
36
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
37
|
+
const mk = (alias, model) => ({
|
|
38
|
+
alias, status: 'working', model, runtime: 'claude-code-cli',
|
|
39
|
+
network_id: nid, project_dir: null,
|
|
40
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
41
|
+
});
|
|
42
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
43
|
+
mk('alpha', 'claude-opus-4'),
|
|
44
|
+
mk('beta', 'gpt-4o'),
|
|
45
|
+
] } });
|
|
46
|
+
});
|
|
47
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
48
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
49
|
+
|
|
50
|
+
const page = await ctx.newPage();
|
|
51
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
52
|
+
await page.waitForSelector('[data-legend-panel-count]', { timeout: 15000 });
|
|
53
|
+
await page.waitForTimeout(300);
|
|
54
|
+
|
|
55
|
+
const probe = await page.evaluate(() => {
|
|
56
|
+
const legendCount = document.querySelector('[data-legend-panel-count]');
|
|
57
|
+
const recentCount = document.querySelector('[data-recent-panel-count]');
|
|
58
|
+
const legendFvn = legendCount ? getComputedStyle(legendCount).fontVariantNumeric : null;
|
|
59
|
+
const recentFvn = recentCount ? getComputedStyle(recentCount).fontVariantNumeric : null;
|
|
60
|
+
const dots = document.querySelectorAll('[data-topo-starfield-dot]');
|
|
61
|
+
const rings = [...document.querySelectorAll('[data-topo-radar-ring]')]
|
|
62
|
+
.map(r => r.getAttribute('data-topo-radar-ring')).sort((a, b) => +a - +b);
|
|
63
|
+
return {
|
|
64
|
+
legendText: legendCount?.textContent ?? null,
|
|
65
|
+
legendFvn,
|
|
66
|
+
legendFontFamily: legendCount ? getComputedStyle(legendCount).fontFamily : null,
|
|
67
|
+
recentFvn,
|
|
68
|
+
starfieldCount: dots.length,
|
|
69
|
+
radarRings: rings,
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
await browser.close();
|
|
73
|
+
|
|
74
|
+
const hasTab = (s) => /tabular-nums/.test(s || '');
|
|
75
|
+
|
|
76
|
+
const results = {
|
|
77
|
+
legend_count_tabular: hasTab(probe.legendFvn),
|
|
78
|
+
legend_count_text_kept: /^\d+ node/.test(probe.legendText || ''),
|
|
79
|
+
legend_count_monospace: /mono/i.test(probe.legendFontFamily || ''),
|
|
80
|
+
recent_count_tabular_sib: hasTab(probe.recentFvn),
|
|
81
|
+
r291_starfield_14: probe.starfieldCount === 14,
|
|
82
|
+
r290_three_rings: JSON.stringify(probe.radarRings) === JSON.stringify(['170', '250', '330']),
|
|
83
|
+
};
|
|
84
|
+
const ok = Object.values(results).every(Boolean);
|
|
85
|
+
console.log(`${ok ? '✅' : '❌'} legend count tabular parity:`, JSON.stringify(results),
|
|
86
|
+
'\n legend text:', JSON.stringify(probe.legendText),
|
|
87
|
+
'\n legend font-variant-numeric:', probe.legendFvn,
|
|
88
|
+
'\n legend font-family:', probe.legendFontFamily,
|
|
89
|
+
'\n recent (sibling) font-variant-numeric:', probe.recentFvn);
|
|
90
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/* Round 274 verification: legend per-row count text gains tabular-nums.
|
|
2
|
+
*
|
|
3
|
+
* Pre-R274 the legend per-row count at x=215 used fontFamily="monospace"
|
|
4
|
+
* (typically tabular by nature) but NO explicit fontVariantNumeric:
|
|
5
|
+
* 'tabular-nums'. The recent-signal panel header flow-count (R225)
|
|
6
|
+
* AND the group-label pip strip (R230) both explicitly set tabular-
|
|
7
|
+
* nums for digit-width stability across 9→10 / 99→100 thresholds.
|
|
8
|
+
*
|
|
9
|
+
* R274 closes the consistency gap: legend per-row counts now also
|
|
10
|
+
* carry fontVariantNumeric: 'tabular-nums'. Belt-and-suspenders for
|
|
11
|
+
* monospace fonts where some implementations have subtle digit-pair
|
|
12
|
+
* variance.
|
|
13
|
+
*
|
|
14
|
+
* Test scope:
|
|
15
|
+
* 1. Working row count text has computed fontVariantNumeric ===
|
|
16
|
+
* 'tabular-nums'.
|
|
17
|
+
* 2. Idle row count text same.
|
|
18
|
+
* 3. Offline row count text same.
|
|
19
|
+
* 4. R273 Layout toggle Grid inactive has hover:text-cyan-300
|
|
20
|
+
* (regression).
|
|
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
|
+
const browser = await chromium.launch({ headless: true });
|
|
27
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
28
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
29
|
+
await ctx.addInitScript(() => {
|
|
30
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
31
|
+
});
|
|
32
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
33
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
34
|
+
const r = await route.fetch();
|
|
35
|
+
const b = await r.json();
|
|
36
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
37
|
+
const mk = (alias, status = 'working') => ({
|
|
38
|
+
alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
39
|
+
network_id: nid, project_dir: null,
|
|
40
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
41
|
+
});
|
|
42
|
+
// Mix of statuses so all 3 legend row counts have non-zero values
|
|
43
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
44
|
+
mk('alpha', 'working'), mk('beta', 'idle'),
|
|
45
|
+
mk('gamma', 'idle'), mk('delta', 'idle'),
|
|
46
|
+
] } });
|
|
47
|
+
});
|
|
48
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
|
|
49
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
50
|
+
|
|
51
|
+
const page = await ctx.newPage();
|
|
52
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
53
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
54
|
+
await page.waitForSelector('[data-legend-count="working"]', { timeout: 10000 });
|
|
55
|
+
await page.waitForSelector('[data-legend-count="idle"]', { timeout: 10000 });
|
|
56
|
+
await page.waitForSelector('[data-legend-count="offline"]', { timeout: 10000 });
|
|
57
|
+
await page.waitForSelector('[data-topo-chrome-layout="grid"]', { timeout: 10000 });
|
|
58
|
+
await page.waitForTimeout(300);
|
|
59
|
+
|
|
60
|
+
const probe = await page.evaluate(() => {
|
|
61
|
+
const working = document.querySelector('[data-legend-count="working"]');
|
|
62
|
+
const idle = document.querySelector('[data-legend-count="idle"]');
|
|
63
|
+
const offline = document.querySelector('[data-legend-count="offline"]');
|
|
64
|
+
const grid = document.querySelector('[data-topo-chrome-layout="grid"]');
|
|
65
|
+
return {
|
|
66
|
+
workingFVN: working ? window.getComputedStyle(working).fontVariantNumeric : null,
|
|
67
|
+
idleFVN: idle ? window.getComputedStyle(idle).fontVariantNumeric : null,
|
|
68
|
+
offlineFVN: offline ? window.getComputedStyle(offline).fontVariantNumeric : null,
|
|
69
|
+
gridClasses: grid ? grid.className.toString() : null,
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
await browser.close();
|
|
73
|
+
|
|
74
|
+
const isTabular = (v) => v != null && v.includes('tabular-nums');
|
|
75
|
+
|
|
76
|
+
const results = {
|
|
77
|
+
working_count_tabular: isTabular(probe.workingFVN),
|
|
78
|
+
idle_count_tabular: isTabular(probe.idleFVN),
|
|
79
|
+
offline_count_tabular: isTabular(probe.offlineFVN),
|
|
80
|
+
r273_grid_has_cyan_hover_text: (probe.gridClasses || '').includes('hover:text-cyan-300'),
|
|
81
|
+
};
|
|
82
|
+
const ok = Object.values(results).every(Boolean);
|
|
83
|
+
console.log(`${ok ? '✅' : '❌'} legend count tabular-nums:`, JSON.stringify(results),
|
|
84
|
+
'\n working FVN:', probe.workingFVN,
|
|
85
|
+
'\n idle FVN: ', probe.idleFVN,
|
|
86
|
+
'\n offline FVN:', probe.offlineFVN);
|
|
87
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/* Round 95 verification: legend rows show live counts. Fleet = 3
|
|
2
|
+
* working + 2 idle + 1 offline. Each row's data-legend-count
|
|
3
|
+
* attribute matches the expected number. Pinning a row brightens
|
|
4
|
+
* its count alongside the label.
|
|
5
|
+
*
|
|
6
|
+
* The count is right-aligned at x=215 (after the flow-arrow swatch
|
|
7
|
+
* which spans x=140-196 on the offline row), pointerEvents:none so
|
|
8
|
+
* the row's R55 hover affordance still hits.
|
|
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 ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
16
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
17
|
+
await ctx.addInitScript(() => {
|
|
18
|
+
try {
|
|
19
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
20
|
+
localStorage.setItem('anet-topo-layout', 'ring');
|
|
21
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
22
|
+
sessionStorage.removeItem('anet-topo-pinned-status');
|
|
23
|
+
} catch {}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
27
|
+
const stale10 = new Date(Date.now() - 10 * 60 * 1000).toISOString();
|
|
28
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
29
|
+
const r = await route.fetch();
|
|
30
|
+
const b = await r.json();
|
|
31
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
32
|
+
const mk = (alias, status, ts) => ({
|
|
33
|
+
alias, status, model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
34
|
+
network_id: nid, project_dir: null,
|
|
35
|
+
created_at: ts, updated_at: ts, last_seen_at: ts,
|
|
36
|
+
});
|
|
37
|
+
const sessions = [
|
|
38
|
+
mk('wkr1', 'working', fresh),
|
|
39
|
+
mk('wkr2', 'working', fresh),
|
|
40
|
+
mk('wkr3', 'working', fresh),
|
|
41
|
+
mk('idl1', 'idle', fresh),
|
|
42
|
+
mk('idl2', 'idle', fresh),
|
|
43
|
+
mk('off1', 'offline', stale10),
|
|
44
|
+
];
|
|
45
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
46
|
+
});
|
|
47
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
48
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.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 === 6, { timeout: 30000 });
|
|
52
|
+
await page.waitForTimeout(400);
|
|
53
|
+
|
|
54
|
+
const counts = () => page.evaluate(() => ({
|
|
55
|
+
working: document.querySelector('[data-legend-count="working"]')?.textContent,
|
|
56
|
+
idle: document.querySelector('[data-legend-count="idle"]')?.textContent,
|
|
57
|
+
offline: document.querySelector('[data-legend-count="offline"]')?.textContent,
|
|
58
|
+
}));
|
|
59
|
+
const opacities = () => page.evaluate(() => ({
|
|
60
|
+
working: +(document.querySelector('[data-legend-count="working"]')?.getAttribute('opacity') || '0'),
|
|
61
|
+
idle: +(document.querySelector('[data-legend-count="idle"]')?.getAttribute('opacity') || '0'),
|
|
62
|
+
offline: +(document.querySelector('[data-legend-count="offline"]')?.getAttribute('opacity') || '0'),
|
|
63
|
+
}));
|
|
64
|
+
|
|
65
|
+
const c = await counts();
|
|
66
|
+
const opsRest = await opacities();
|
|
67
|
+
|
|
68
|
+
// Pin the working row — its count should brighten.
|
|
69
|
+
await page.locator('[data-legend-status="working"]').click();
|
|
70
|
+
await page.waitForTimeout(250);
|
|
71
|
+
const opsPinned = await opacities();
|
|
72
|
+
|
|
73
|
+
await browser.close();
|
|
74
|
+
|
|
75
|
+
const results = {
|
|
76
|
+
workingCount_3: c.working === '3',
|
|
77
|
+
idleCount_2: c.idle === '2',
|
|
78
|
+
offlineCount_1: c.offline === '1',
|
|
79
|
+
resting_dim: opsRest.working < 0.7 && opsRest.idle < 0.7 && opsRest.offline < 0.7,
|
|
80
|
+
pinnedWorking_bright: opsPinned.working >= 0.9,
|
|
81
|
+
pinnedWorking_othersUnchanged: opsPinned.idle < 0.7 && opsPinned.offline < 0.7,
|
|
82
|
+
};
|
|
83
|
+
const ok = Object.values(results).every(Boolean);
|
|
84
|
+
console.log(`${ok ? '✅' : '❌'} legend counts:`, JSON.stringify(results),
|
|
85
|
+
`\n counts=`, c,
|
|
86
|
+
`\n opsRest=`, opsRest,
|
|
87
|
+
`\n opsPinned=`, opsPinned);
|
|
88
|
+
process.exit(ok ? 0 : 1);
|