@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,157 @@
|
|
|
1
|
+
/* Round 194 verification: node label card picks up drop-shadow
|
|
2
|
+
* elevation that intensifies on node hover, mirroring R135's panel
|
|
3
|
+
* hover-elevation but at per-node grain.
|
|
4
|
+
*
|
|
5
|
+
* Pre-R194 the card lifted 1.5 px on hover (R26) but had no shadow
|
|
6
|
+
* follow — the elevation gesture lacked physical weight. R194 adds
|
|
7
|
+
* rest : drop-shadow(0 1px 2px rgba(...,0.30 dark / 0.08 light))
|
|
8
|
+
* hover : drop-shadow(0 4px 12px rgba(0,0,0,0.60) dark
|
|
9
|
+
* / 0 3px 8px rgba(15,23,42,0.20) light)
|
|
10
|
+
* with transition: filter 220ms ease-out.
|
|
11
|
+
*
|
|
12
|
+
* Test (cyber/dark theme, 4 sessions = card mode):
|
|
13
|
+
* 1. data-node-label-card present + matches alias
|
|
14
|
+
* 2. Idle elevation attr = 'idle'
|
|
15
|
+
* 3. Idle filter style includes 'drop-shadow' (rest shadow on)
|
|
16
|
+
* 4. Idle blur radius is small (1-2 px range)
|
|
17
|
+
* 5. transition: filter 220ms ease-out
|
|
18
|
+
* 6. Hover the node — wait past 250ms
|
|
19
|
+
* 7. data-node-label-card-elevation flips to 'hover'
|
|
20
|
+
* 8. Hover filter has bigger blur (>= 8px) — drop-shadow strengthened
|
|
21
|
+
* 9. Other (non-hovered) cards stay 'idle'
|
|
22
|
+
* 10. Unhover — flips back to 'idle'
|
|
23
|
+
* 11. Filter transition string still present after settle
|
|
24
|
+
*/
|
|
25
|
+
import { chromium } from 'playwright';
|
|
26
|
+
import { readFileSync } from 'node:fs';
|
|
27
|
+
|
|
28
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
29
|
+
const browser = await chromium.launch({ headless: true });
|
|
30
|
+
|
|
31
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
32
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
33
|
+
await ctx.addInitScript(() => {
|
|
34
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
38
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
39
|
+
const r = await route.fetch();
|
|
40
|
+
const b = await r.json();
|
|
41
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
42
|
+
const mk = (alias) => ({
|
|
43
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
44
|
+
network_id: nid, project_dir: null,
|
|
45
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
46
|
+
});
|
|
47
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
48
|
+
mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
|
|
49
|
+
] } });
|
|
50
|
+
});
|
|
51
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
52
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
53
|
+
|
|
54
|
+
const page = await ctx.newPage();
|
|
55
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
56
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
57
|
+
await page.waitForSelector('[data-node-label-card]', { timeout: 10000 });
|
|
58
|
+
await page.waitForTimeout(400);
|
|
59
|
+
|
|
60
|
+
// Probe idle state of alpha's label card
|
|
61
|
+
const idleAlpha = await page.evaluate(() => {
|
|
62
|
+
const el = document.querySelector('[data-node-label-card="alpha"]');
|
|
63
|
+
if (!el) return null;
|
|
64
|
+
const cs = getComputedStyle(el);
|
|
65
|
+
return {
|
|
66
|
+
alias: el.getAttribute('data-node-label-card'),
|
|
67
|
+
elevation: el.getAttribute('data-node-label-card-elevation'),
|
|
68
|
+
filter: cs.filter,
|
|
69
|
+
transition: cs.transitionProperty + ' / ' + cs.transitionDuration,
|
|
70
|
+
inlineFilter: el.style.filter,
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Hover the alpha node's <g> wrapper
|
|
75
|
+
await page.hover('g[data-node="alpha"]');
|
|
76
|
+
await page.waitForTimeout(280); // past 220ms transition
|
|
77
|
+
|
|
78
|
+
const hoveredAlpha = await page.evaluate(() => {
|
|
79
|
+
const el = document.querySelector('[data-node-label-card="alpha"]');
|
|
80
|
+
const cs = getComputedStyle(el);
|
|
81
|
+
return {
|
|
82
|
+
elevation: el.getAttribute('data-node-label-card-elevation'),
|
|
83
|
+
filter: cs.filter,
|
|
84
|
+
inlineFilter: el.style.filter,
|
|
85
|
+
};
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Probe non-hovered card (beta) stays idle
|
|
89
|
+
const idleBeta = await page.evaluate(() => {
|
|
90
|
+
const el = document.querySelector('[data-node-label-card="beta"]');
|
|
91
|
+
return {
|
|
92
|
+
elevation: el.getAttribute('data-node-label-card-elevation'),
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Move away, wait for transition back
|
|
97
|
+
await page.mouse.move(0, 0);
|
|
98
|
+
await page.waitForTimeout(280);
|
|
99
|
+
|
|
100
|
+
const settledAlpha = await page.evaluate(() => {
|
|
101
|
+
const el = document.querySelector('[data-node-label-card="alpha"]');
|
|
102
|
+
const cs = getComputedStyle(el);
|
|
103
|
+
return {
|
|
104
|
+
elevation: el.getAttribute('data-node-label-card-elevation'),
|
|
105
|
+
filter: cs.filter,
|
|
106
|
+
transition: cs.transitionProperty + ' / ' + cs.transitionDuration,
|
|
107
|
+
};
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
await browser.close();
|
|
111
|
+
|
|
112
|
+
// Parse blur radius out of drop-shadow(). Chrome's getComputedStyle
|
|
113
|
+
// canonicalises into `drop-shadow(rgba(...) 0px Ypx BLURpx)` with the
|
|
114
|
+
// color first — the standard write-order `drop-shadow(0 Ypx BLURpx
|
|
115
|
+
// rgba(...))` is rearranged. So scan all numeric tokens inside the
|
|
116
|
+
// drop-shadow() parens with rgba/hsla content stripped first; blur is
|
|
117
|
+
// the 3rd numeric value.
|
|
118
|
+
function parseShadowBlur(filterStr) {
|
|
119
|
+
if (!filterStr) return null;
|
|
120
|
+
const m = filterStr.match(/drop-shadow\(([\s\S]+)\)\s*$/);
|
|
121
|
+
if (!m) return null;
|
|
122
|
+
const stripped = m[1].replace(/(rgba?|hsla?)\([^)]*\)/g, '');
|
|
123
|
+
const nums = (stripped.match(/-?\d+(?:\.\d+)?/g) || []).map(Number);
|
|
124
|
+
return nums.length >= 3 ? Math.abs(nums[2]) : null;
|
|
125
|
+
}
|
|
126
|
+
const idleBlur = parseShadowBlur(idleAlpha?.filter);
|
|
127
|
+
const hoverBlur = parseShadowBlur(hoveredAlpha?.filter);
|
|
128
|
+
const settledBlur = parseShadowBlur(settledAlpha?.filter);
|
|
129
|
+
|
|
130
|
+
const results = {
|
|
131
|
+
card_present: !!idleAlpha,
|
|
132
|
+
alias_matches: idleAlpha?.alias === 'alpha',
|
|
133
|
+
idle_elevation_attr: idleAlpha?.elevation === 'idle',
|
|
134
|
+
idle_has_dropshadow: (idleAlpha?.filter || '').includes('drop-shadow'),
|
|
135
|
+
idle_blur_small: idleBlur !== null && idleBlur > 0 && idleBlur <= 3,
|
|
136
|
+
filter_transition: idleAlpha?.transition.includes('filter') && idleAlpha?.transition.includes('0.22s'),
|
|
137
|
+
|
|
138
|
+
hover_elevation_attr: hoveredAlpha?.elevation === 'hover',
|
|
139
|
+
hover_has_dropshadow: (hoveredAlpha?.filter || '').includes('drop-shadow'),
|
|
140
|
+
hover_blur_larger: hoverBlur !== null && hoverBlur >= 8,
|
|
141
|
+
hover_blur_grew: idleBlur !== null && hoverBlur !== null && hoverBlur > idleBlur,
|
|
142
|
+
hover_filter_changed: idleAlpha?.filter !== hoveredAlpha?.filter,
|
|
143
|
+
|
|
144
|
+
beta_stays_idle: idleBeta?.elevation === 'idle',
|
|
145
|
+
|
|
146
|
+
settled_back_to_idle: settledAlpha?.elevation === 'idle',
|
|
147
|
+
settled_blur_small: settledBlur !== null && settledBlur <= 3,
|
|
148
|
+
settled_keeps_transition: settledAlpha?.transition.includes('filter'),
|
|
149
|
+
};
|
|
150
|
+
const ok = Object.values(results).every(Boolean);
|
|
151
|
+
console.log(`${ok ? '✅' : '❌'} node label card elevation:`, JSON.stringify(results),
|
|
152
|
+
`\n idleAlpha:`, idleAlpha,
|
|
153
|
+
`\n hoveredAlpha:`, hoveredAlpha,
|
|
154
|
+
`\n idleBeta:`, idleBeta,
|
|
155
|
+
`\n settledAlpha:`, settledAlpha,
|
|
156
|
+
`\n blurs:`, { idleBlur, hoverBlur, settledBlur });
|
|
157
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/* Round 217 verification: node label card stroke tints to
|
|
2
|
+
* legendAccent (cyan) on parent-node hover, adding a 3rd hover-
|
|
3
|
+
* feedback channel alongside R26 lift + R194 drop-shadow.
|
|
4
|
+
*
|
|
5
|
+
* Pre-R217 only 2 channels responded (translateY + drop-shadow);
|
|
6
|
+
* stroke stayed constant at pal.labelBox.stroke. R217 adds the
|
|
7
|
+
* 3rd channel for full hover-elevation triple coverage.
|
|
8
|
+
*
|
|
9
|
+
* Test (cyber dark, 4 working sessions = card mode):
|
|
10
|
+
* 1. data-node-label-card present + 4 alias matches.
|
|
11
|
+
* 2. Idle alpha: elevation='idle', stroke == pal.labelBox.stroke
|
|
12
|
+
* (NOT cyan)
|
|
13
|
+
* 3. transition includes BOTH 'filter 220ms' AND 'stroke 220ms'
|
|
14
|
+
* 4. Hover g[data-node="alpha"] → wait 250ms
|
|
15
|
+
* 5. alpha elevation='hover', stroke shifted to legendAccent
|
|
16
|
+
* (cyan-leaning — B > R in computed color)
|
|
17
|
+
* 6. Beta (non-hovered) card stroke STILL pal.labelBox.stroke
|
|
18
|
+
* 7. Move away → alpha stroke back to pal.labelBox.stroke
|
|
19
|
+
*/
|
|
20
|
+
import { chromium } from 'playwright';
|
|
21
|
+
import { readFileSync } from 'node:fs';
|
|
22
|
+
|
|
23
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
24
|
+
const browser = await chromium.launch({ headless: true });
|
|
25
|
+
|
|
26
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
27
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
28
|
+
await ctx.addInitScript(() => {
|
|
29
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
30
|
+
});
|
|
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) => ({
|
|
38
|
+
alias, status: 'working', 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
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
43
|
+
mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
|
|
44
|
+
] } });
|
|
45
|
+
});
|
|
46
|
+
await ctx.route('**/api/hub/messages*', (r2) => r2.fulfill({ json: { messages: [] } }));
|
|
47
|
+
await ctx.route('**/api/hub/tasks*', (r2) => r2.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 === 4, { timeout: 30000 });
|
|
52
|
+
await page.waitForSelector('[data-node-label-card]', { timeout: 10000, state: 'attached' });
|
|
53
|
+
await page.waitForTimeout(300);
|
|
54
|
+
|
|
55
|
+
const probe = async () => page.evaluate(() => {
|
|
56
|
+
const cards = [...document.querySelectorAll('[data-node-label-card]')];
|
|
57
|
+
return cards.map(el => ({
|
|
58
|
+
alias: el.getAttribute('data-node-label-card'),
|
|
59
|
+
elevation: el.getAttribute('data-node-label-card-elevation'),
|
|
60
|
+
stroke: el.getAttribute('stroke'),
|
|
61
|
+
computedStroke: getComputedStyle(el).stroke,
|
|
62
|
+
transition: el.style.transition,
|
|
63
|
+
}));
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const idle = await probe();
|
|
67
|
+
|
|
68
|
+
await page.hover('g[data-node="alpha"]');
|
|
69
|
+
await page.waitForTimeout(260);
|
|
70
|
+
const hovered = await probe();
|
|
71
|
+
|
|
72
|
+
await page.mouse.move(0, 0);
|
|
73
|
+
await page.waitForTimeout(260);
|
|
74
|
+
const settled = await probe();
|
|
75
|
+
|
|
76
|
+
await browser.close();
|
|
77
|
+
|
|
78
|
+
// Detect "cyan-leaning" — RGB or OKLab parse, blue/teal axis dominant
|
|
79
|
+
function isCyanish(s) {
|
|
80
|
+
if (!s) return false;
|
|
81
|
+
let m = s.match(/rgba?\(\s*(\d+)\s*,?\s*(\d+)\s*,?\s*(\d+)/);
|
|
82
|
+
if (m) {
|
|
83
|
+
const r = +m[1], g = +m[2], b = +m[3];
|
|
84
|
+
// cyan/teal: B and G both clearly > R
|
|
85
|
+
return (b - r >= 50 && g - r >= 30) || (b > r + 60);
|
|
86
|
+
}
|
|
87
|
+
m = s.match(/oklab\(\s*([\-\d.]+)\s+([\-\d.]+)\s+([\-\d.]+)/);
|
|
88
|
+
if (m) {
|
|
89
|
+
const a = +m[2], b = +m[3];
|
|
90
|
+
return a < -0.05 || b < -0.05;
|
|
91
|
+
}
|
|
92
|
+
m = s.match(/oklch\(\s*([\-\d.]+)\s+([\-\d.]+)\s+([\-\d.]+)/);
|
|
93
|
+
if (m) {
|
|
94
|
+
const h = +m[3];
|
|
95
|
+
// cyan/teal hues are roughly 180-220 in OKLCH
|
|
96
|
+
return h > 150 && h < 240 && parseFloat(m[2]) > 0.05;
|
|
97
|
+
}
|
|
98
|
+
m = s.match(/^#([0-9a-f]{6})/i);
|
|
99
|
+
if (m) {
|
|
100
|
+
const v = parseInt(m[1], 16);
|
|
101
|
+
const r = (v >> 16) & 0xff, g = (v >> 8) & 0xff, b = v & 0xff;
|
|
102
|
+
return b - r >= 50 && g - r >= 30;
|
|
103
|
+
}
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const idleAlpha = idle.find(c => c.alias === 'alpha');
|
|
108
|
+
const idleBeta = idle.find(c => c.alias === 'beta');
|
|
109
|
+
const hovAlpha = hovered.find(c => c.alias === 'alpha');
|
|
110
|
+
const hovBeta = hovered.find(c => c.alias === 'beta');
|
|
111
|
+
const settledAlpha = settled.find(c => c.alias === 'alpha');
|
|
112
|
+
|
|
113
|
+
const results = {
|
|
114
|
+
four_cards_present: idle.length === 4,
|
|
115
|
+
idle_alpha_elevation: idleAlpha?.elevation === 'idle',
|
|
116
|
+
idle_alpha_stroke_not_cyan: !isCyanish(idleAlpha?.computedStroke),
|
|
117
|
+
transition_has_filter: /filter\s+(?:220ms|0\.22s)/.test(idleAlpha?.transition || ''),
|
|
118
|
+
transition_has_stroke: /stroke\s+(?:220ms|0\.22s)/.test(idleAlpha?.transition || ''),
|
|
119
|
+
|
|
120
|
+
hover_alpha_elevation: hovAlpha?.elevation === 'hover',
|
|
121
|
+
hover_alpha_stroke_cyan: isCyanish(hovAlpha?.computedStroke),
|
|
122
|
+
hover_alpha_stroke_changed: idleAlpha?.computedStroke !== hovAlpha?.computedStroke,
|
|
123
|
+
hover_beta_stays_neutral: hovBeta?.elevation === 'idle'
|
|
124
|
+
&& !isCyanish(hovBeta?.computedStroke),
|
|
125
|
+
|
|
126
|
+
settled_alpha_back_to_idle: settledAlpha?.elevation === 'idle',
|
|
127
|
+
settled_alpha_stroke_neutral: !isCyanish(settledAlpha?.computedStroke),
|
|
128
|
+
};
|
|
129
|
+
const ok = Object.values(results).every(Boolean);
|
|
130
|
+
console.log(`${ok ? '✅' : '❌'} node label card stroke:`, JSON.stringify(results),
|
|
131
|
+
`\n idleAlpha:`, idleAlpha,
|
|
132
|
+
`\n hovAlpha:`, hovAlpha,
|
|
133
|
+
`\n hovBeta:`, hovBeta,
|
|
134
|
+
`\n settledAlpha:`, settledAlpha);
|
|
135
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/* Round 167 verification: node status-ring picks up stroke-width
|
|
2
|
+
* transition.
|
|
3
|
+
*
|
|
4
|
+
* Pre-R167 the node outer ring (cx,cy,r,fill,stroke + binary
|
|
5
|
+
* stroke-width 3/1.5 + dasharray) had transition-[fill,stroke]
|
|
6
|
+
* duration-300 — colors transitioned but stroke-width snapped
|
|
7
|
+
* when a session went working → offline. R167 adds stroke-width
|
|
8
|
+
* to the transition list (300ms ease-out), symmetric with R165
|
|
9
|
+
* (pressure-bar width) and R166 (edge stroke-width).
|
|
10
|
+
*
|
|
11
|
+
* Test:
|
|
12
|
+
* 1. Mock 2 working + 2 offline sessions
|
|
13
|
+
* 2. Probe each node's status ring (via data-node-status-ring)
|
|
14
|
+
* 3. Working ring: strokeWidth=3, transition includes
|
|
15
|
+
* 'stroke-width 300ms'
|
|
16
|
+
* 4. Offline ring: strokeWidth=1.5, dasharray='5 5',
|
|
17
|
+
* transition includes 'stroke-width 300ms'
|
|
18
|
+
* 5. Both rings expose data-node-status-ring with the status
|
|
19
|
+
* label
|
|
20
|
+
*
|
|
21
|
+
* The DOM attribute reflects the React-rendered value, not the
|
|
22
|
+
* CSS-interpolated value during transitions — so the R51
|
|
23
|
+
* overlap-test's stroke-width=3 / 1.5 selectors still match.
|
|
24
|
+
*/
|
|
25
|
+
import { chromium } from 'playwright';
|
|
26
|
+
import { readFileSync } from 'node:fs';
|
|
27
|
+
|
|
28
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
29
|
+
const browser = await chromium.launch({ headless: true });
|
|
30
|
+
|
|
31
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
32
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
33
|
+
await ctx.addInitScript(() => {
|
|
34
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
38
|
+
const stale = new Date(Date.now() - 10 * 60 * 1000).toISOString();
|
|
39
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
40
|
+
const r = await route.fetch();
|
|
41
|
+
const b = await r.json();
|
|
42
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
43
|
+
const mk = (alias, status, last) => ({
|
|
44
|
+
alias, status, model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
45
|
+
network_id: nid, project_dir: null,
|
|
46
|
+
created_at: last, updated_at: last, last_seen_at: last,
|
|
47
|
+
});
|
|
48
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
49
|
+
mk('w1', 'working', fresh), mk('w2', 'working', fresh),
|
|
50
|
+
mk('o1', 'offline', stale), mk('o2', 'offline', stale),
|
|
51
|
+
] } });
|
|
52
|
+
});
|
|
53
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
54
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.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-node-status-ring]', { timeout: 10000 });
|
|
60
|
+
await page.waitForTimeout(400);
|
|
61
|
+
|
|
62
|
+
const probe = await page.evaluate(() => {
|
|
63
|
+
const rings = [...document.querySelectorAll('[data-node-status-ring]')];
|
|
64
|
+
return rings.map(r => ({
|
|
65
|
+
label: r.getAttribute('data-node-status-ring'),
|
|
66
|
+
strokeWidth: parseFloat(r.getAttribute('stroke-width')),
|
|
67
|
+
dasharray: r.getAttribute('stroke-dasharray'),
|
|
68
|
+
inlineTransition: r.style.transition || '',
|
|
69
|
+
computedTransition: getComputedStyle(r).transition || '',
|
|
70
|
+
}));
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
await browser.close();
|
|
74
|
+
|
|
75
|
+
const working = probe.filter(p => p.label === 'working');
|
|
76
|
+
const offline = probe.filter(p => p.label === 'offline');
|
|
77
|
+
|
|
78
|
+
const allHaveStrokeWidth = probe.every(p =>
|
|
79
|
+
p.inlineTransition.includes('stroke-width 300ms') ||
|
|
80
|
+
/stroke-width\s+0\.3s|stroke-width\s+300ms/.test(p.computedTransition));
|
|
81
|
+
const allHaveFill = probe.every(p =>
|
|
82
|
+
p.inlineTransition.includes('fill 300ms') ||
|
|
83
|
+
/fill\s+0\.3s|fill\s+300ms/.test(p.computedTransition));
|
|
84
|
+
const allHaveStroke = probe.every(p =>
|
|
85
|
+
p.inlineTransition.includes('stroke 300ms') ||
|
|
86
|
+
/[^-]stroke\s+0\.3s|[^-]stroke\s+300ms/.test(p.computedTransition));
|
|
87
|
+
|
|
88
|
+
const results = {
|
|
89
|
+
four_rings_found: probe.length === 4,
|
|
90
|
+
two_working_rings: working.length === 2,
|
|
91
|
+
two_offline_rings: offline.length === 2,
|
|
92
|
+
working_strokeWidth_3: working.every(p => p.strokeWidth === 3),
|
|
93
|
+
offline_strokeWidth_15: offline.every(p => p.strokeWidth === 1.5),
|
|
94
|
+
working_no_dasharray: working.every(p => p.dasharray === 'none' || !p.dasharray),
|
|
95
|
+
offline_dasharray_5_5: offline.every(p => p.dasharray === '5 5'),
|
|
96
|
+
all_have_stroke_width_transition: allHaveStrokeWidth,
|
|
97
|
+
all_have_fill_transition: allHaveFill,
|
|
98
|
+
all_have_stroke_transition: allHaveStroke,
|
|
99
|
+
};
|
|
100
|
+
const ok = Object.values(results).every(Boolean);
|
|
101
|
+
console.log(`${ok ? '✅' : '❌'} node status-ring transition:`, JSON.stringify(results),
|
|
102
|
+
`\n rings=`, probe);
|
|
103
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/* Round 211 verification: node alias-card text fills (alias + sub-
|
|
2
|
+
* status label) transition fill on status flip, matching R167
|
|
3
|
+
* status-ring fill 300ms. Pre-R211 the ring smoothly recolored
|
|
4
|
+
* while the text snap-cut to the new tier hue.
|
|
5
|
+
*
|
|
6
|
+
* Test (cyber dark, 4 sessions in card mode):
|
|
7
|
+
* 1. 4 [data-node-alias-text] alias texts present, 4
|
|
8
|
+
* [data-node-sub-text] sub texts present.
|
|
9
|
+
* 2. Each has inline style.transition: 'fill 300ms ease-out'.
|
|
10
|
+
* 3. Alias fill matches the session's status.text colour family
|
|
11
|
+
* (working green-ish, idle teal-ish, offline gray-ish).
|
|
12
|
+
* 4. Sub fill matches status.primary (similar tier hue).
|
|
13
|
+
* 5. transition value is identical across all alias texts
|
|
14
|
+
* (signals consistent timing — not per-node drift).
|
|
15
|
+
* 6. Truncate verified — alias rendered text matches the
|
|
16
|
+
* session.alias (no extra chars, no missing).
|
|
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
|
+
|
|
24
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
25
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
26
|
+
await ctx.addInitScript(() => {
|
|
27
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
31
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
32
|
+
const r = await route.fetch();
|
|
33
|
+
const b = await r.json();
|
|
34
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
35
|
+
const mk = (alias, status) => ({
|
|
36
|
+
alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
37
|
+
network_id: nid, project_dir: null,
|
|
38
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
39
|
+
});
|
|
40
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
41
|
+
mk('alpha', 'working'),
|
|
42
|
+
mk('beta', 'working'),
|
|
43
|
+
mk('gamma', 'idle'),
|
|
44
|
+
mk('delta', 'offline'),
|
|
45
|
+
] } });
|
|
46
|
+
});
|
|
47
|
+
await ctx.route('**/api/hub/messages*', (r2) => r2.fulfill({ json: { messages: [] } }));
|
|
48
|
+
await ctx.route('**/api/hub/tasks*', (r2) => r2.fulfill({ json: { tasks: [] } }));
|
|
49
|
+
|
|
50
|
+
const page = await ctx.newPage();
|
|
51
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
52
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
53
|
+
await page.waitForSelector('[data-node-alias-text]', { timeout: 10000, state: 'attached' });
|
|
54
|
+
await page.waitForTimeout(300);
|
|
55
|
+
|
|
56
|
+
const probe = await page.evaluate(() => {
|
|
57
|
+
const aliasTexts = [...document.querySelectorAll('[data-node-alias-text]')];
|
|
58
|
+
const subTexts = [...document.querySelectorAll('[data-node-sub-text]')];
|
|
59
|
+
return {
|
|
60
|
+
alias: aliasTexts.map(el => ({
|
|
61
|
+
alias: el.getAttribute('data-node-alias-text'),
|
|
62
|
+
text: el.textContent,
|
|
63
|
+
fill: el.getAttribute('fill'),
|
|
64
|
+
transition: el.style.transition,
|
|
65
|
+
})),
|
|
66
|
+
sub: subTexts.map(el => ({
|
|
67
|
+
alias: el.getAttribute('data-node-sub-text'),
|
|
68
|
+
text: el.textContent,
|
|
69
|
+
fill: el.getAttribute('fill'),
|
|
70
|
+
transition: el.style.transition,
|
|
71
|
+
})),
|
|
72
|
+
};
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
await browser.close();
|
|
76
|
+
|
|
77
|
+
const hasFillT = (s) => /fill\s+(?:300ms|0\.3s)\s+ease-out/.test(s || '');
|
|
78
|
+
|
|
79
|
+
const aliasAlpha = probe.alias.find(t => t.alias === 'alpha');
|
|
80
|
+
const aliasGamma = probe.alias.find(t => t.alias === 'gamma');
|
|
81
|
+
const aliasDelta = probe.alias.find(t => t.alias === 'delta');
|
|
82
|
+
const subAlpha = probe.sub.find(t => t.alias === 'alpha');
|
|
83
|
+
const subDelta = probe.sub.find(t => t.alias === 'delta');
|
|
84
|
+
|
|
85
|
+
const results = {
|
|
86
|
+
four_alias_texts: probe.alias.length === 4,
|
|
87
|
+
four_sub_texts: probe.sub.length === 4,
|
|
88
|
+
|
|
89
|
+
alpha_alias_renders: aliasAlpha?.text === 'alpha',
|
|
90
|
+
gamma_alias_renders: aliasGamma?.text === 'gamma',
|
|
91
|
+
delta_alias_renders: aliasDelta?.text === 'delta',
|
|
92
|
+
|
|
93
|
+
// All alias texts have fill transition
|
|
94
|
+
all_alias_have_fill_transition: probe.alias.every(t => hasFillT(t.transition)),
|
|
95
|
+
all_sub_have_fill_transition: probe.sub.every(t => hasFillT(t.transition)),
|
|
96
|
+
|
|
97
|
+
// Working alpha vs offline delta — fill differs (different tier color)
|
|
98
|
+
alpha_delta_fill_differs: aliasAlpha?.fill && aliasDelta?.fill
|
|
99
|
+
&& aliasAlpha.fill !== aliasDelta.fill,
|
|
100
|
+
|
|
101
|
+
// Working vs idle — fill differs (working green / idle teal in palette)
|
|
102
|
+
alpha_gamma_fill_differs: aliasAlpha?.fill && aliasGamma?.fill
|
|
103
|
+
&& aliasAlpha.fill !== aliasGamma.fill,
|
|
104
|
+
|
|
105
|
+
// Sub text fill present (non-empty)
|
|
106
|
+
sub_fills_present: probe.sub.every(t => !!t.fill && t.fill !== 'none'),
|
|
107
|
+
|
|
108
|
+
// Sub text content reflects status label (working / idle / offline)
|
|
109
|
+
sub_alpha_has_working: /working|sse/i.test(subAlpha?.text || ''),
|
|
110
|
+
sub_delta_has_offline: /offline/i.test(subDelta?.text || ''),
|
|
111
|
+
|
|
112
|
+
// Transition uniformity — all alias texts share the same transition string
|
|
113
|
+
alias_transition_uniform: new Set(probe.alias.map(t => t.transition)).size === 1,
|
|
114
|
+
};
|
|
115
|
+
const ok = Object.values(results).every(Boolean);
|
|
116
|
+
console.log(`${ok ? '✅' : '❌'} node text fill transition:`, JSON.stringify(results),
|
|
117
|
+
`\n alias:`, probe.alias,
|
|
118
|
+
`\n sub:`, probe.sub);
|
|
119
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/* Round 147 verification: node tooltip extends R98's flow summary
|
|
2
|
+
* with the actual sender / receiver alias breakdown. The R97 idiom
|
|
3
|
+
* — anywhere the UI shows "N" should hover-explain WHICH N — was
|
|
4
|
+
* applied to filter pills, group labels, vendor letters, pressure
|
|
5
|
+
* segments, recent-row, active-links chip. The node title was the
|
|
6
|
+
* last surface showing only aggregate counts ("12 in / 5 out");
|
|
7
|
+
* R147 adds peer lines.
|
|
8
|
+
*
|
|
9
|
+
* Fleet:
|
|
10
|
+
* alpha — sends 3 msgs to beta, 5 to gamma, 2 to delta
|
|
11
|
+
* beta — sends 4 msgs to alpha, 1 to gamma
|
|
12
|
+
* gamma — quiet (receiver only)
|
|
13
|
+
* delta — quiet (receiver only)
|
|
14
|
+
*
|
|
15
|
+
* For alpha:
|
|
16
|
+
* flowOut = 3 + 5 + 2 = 10
|
|
17
|
+
* flowIn = 4 (from beta)
|
|
18
|
+
* senders ← beta (4)
|
|
19
|
+
* receivers → beta (3), gamma (5), delta (2) — sorted by count desc:
|
|
20
|
+
* gamma (5), beta (3), delta (2)
|
|
21
|
+
*
|
|
22
|
+
* Title should contain:
|
|
23
|
+
* "flows: 4 in / 10 out"
|
|
24
|
+
* "← from: beta (4)"
|
|
25
|
+
* "→ to: gamma (5), beta (3), delta (2)" (sorted desc by count)
|
|
26
|
+
*
|
|
27
|
+
* Also verify a node with no flows (gamma will have 5 inbound from alpha
|
|
28
|
+
* and 1 from beta; let's pick delta which only has 2 inbound from alpha).
|
|
29
|
+
*/
|
|
30
|
+
import { chromium } from 'playwright';
|
|
31
|
+
import { readFileSync } from 'node:fs';
|
|
32
|
+
|
|
33
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
34
|
+
const browser = await chromium.launch({ headless: true });
|
|
35
|
+
|
|
36
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, 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
|
+
|
|
42
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
43
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
44
|
+
const r = await route.fetch();
|
|
45
|
+
const b = await r.json();
|
|
46
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
47
|
+
const mk = (alias, status) => ({
|
|
48
|
+
alias, status, model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
49
|
+
network_id: nid, project_dir: null,
|
|
50
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
51
|
+
});
|
|
52
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
53
|
+
mk('alpha', 'working'), mk('beta', 'working'),
|
|
54
|
+
mk('gamma', 'idle'), mk('delta', 'idle'),
|
|
55
|
+
] } });
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const now = Date.now();
|
|
59
|
+
const mkMsg = (id, from, to, ageMs) => ({
|
|
60
|
+
id, from_alias: from, to_alias: to, content: 'hi',
|
|
61
|
+
network_id: 'default', created_at: new Date(now - ageMs).toISOString(),
|
|
62
|
+
});
|
|
63
|
+
const msgs = [];
|
|
64
|
+
let id = 0;
|
|
65
|
+
// alpha → beta (3), gamma (5), delta (2)
|
|
66
|
+
for (let i = 0; i < 3; i++) msgs.push(mkMsg(`m${id++}`, 'alpha', 'beta', 10000 + id * 100));
|
|
67
|
+
for (let i = 0; i < 5; i++) msgs.push(mkMsg(`m${id++}`, 'alpha', 'gamma', 10000 + id * 100));
|
|
68
|
+
for (let i = 0; i < 2; i++) msgs.push(mkMsg(`m${id++}`, 'alpha', 'delta', 10000 + id * 100));
|
|
69
|
+
// beta → alpha (4), gamma (1)
|
|
70
|
+
for (let i = 0; i < 4; i++) msgs.push(mkMsg(`m${id++}`, 'beta', 'alpha', 10000 + id * 100));
|
|
71
|
+
for (let i = 0; i < 1; i++) msgs.push(mkMsg(`m${id++}`, 'beta', 'gamma', 10000 + id * 100));
|
|
72
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
|
|
73
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
74
|
+
|
|
75
|
+
const page = await ctx.newPage();
|
|
76
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
77
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
78
|
+
await page.waitForTimeout(400);
|
|
79
|
+
|
|
80
|
+
const readTitle = (alias) => page.evaluate((a) => {
|
|
81
|
+
const node = document.querySelector(`g[data-node="${a}"]`);
|
|
82
|
+
return node?.querySelector('title')?.textContent || '';
|
|
83
|
+
}, alias);
|
|
84
|
+
|
|
85
|
+
const alphaTitle = await readTitle('alpha');
|
|
86
|
+
const deltaTitle = await readTitle('delta');
|
|
87
|
+
|
|
88
|
+
await browser.close();
|
|
89
|
+
|
|
90
|
+
const results = {
|
|
91
|
+
// alpha: flows aggregate
|
|
92
|
+
alpha_flowSummary: alphaTitle.includes('flows: 4 in / 10 out'),
|
|
93
|
+
// alpha: senders line
|
|
94
|
+
alpha_sendersLine: alphaTitle.includes('← from: beta (4)'),
|
|
95
|
+
// alpha: receivers line — must be sorted desc by count (gamma 5, beta 3, delta 2)
|
|
96
|
+
alpha_receiversOrder: alphaTitle.includes('→ to: gamma (5), beta (3), delta (2)'),
|
|
97
|
+
|
|
98
|
+
// delta: receiver only, only inbound from alpha (2 msgs)
|
|
99
|
+
delta_flowSummary: deltaTitle.includes('flows: 2 in / 0 out'),
|
|
100
|
+
delta_sendersLine: deltaTitle.includes('← from: alpha (2)'),
|
|
101
|
+
delta_noReceivers: !deltaTitle.includes('→ to:'),
|
|
102
|
+
};
|
|
103
|
+
const ok = Object.values(results).every(Boolean);
|
|
104
|
+
console.log(`${ok ? '✅' : '❌'} node title peers:`, JSON.stringify(results),
|
|
105
|
+
`\n alpha title:\n${alphaTitle}`,
|
|
106
|
+
`\n delta title:\n${deltaTitle}`);
|
|
107
|
+
process.exit(ok ? 0 : 1);
|