@sleep2agi/agent-network-dashboard 0.4.6-preview.18 → 0.4.6-preview.180
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_ID +1 -1
- package/.next/app-path-routes-manifest.json +1 -0
- 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/routes-manifest.json +6 -0
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +2 -2
- package/.next/server/app/_not-found.rsc +12 -12
- package/.next/server/app/_not-found.segments/_full.segment.rsc +12 -12
- package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/server/app/_not-found.segments/_index.segment.rsc +7 -7
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
- package/.next/server/app/admin.html +2 -2
- package/.next/server/app/admin.rsc +14 -14
- package/.next/server/app/admin.segments/_full.segment.rsc +14 -14
- package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
- package/.next/server/app/admin.segments/_index.segment.rsc +7 -7
- package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
- package/.next/server/app/api/hub/servers/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/hub/servers/route/build-manifest.json +9 -0
- package/.next/server/app/api/hub/servers/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/hub/servers/route.js +7 -0
- package/.next/server/app/api/hub/servers/route.js.map +5 -0
- package/.next/server/app/api/hub/servers/route.js.nft.json +1 -0
- package/.next/server/app/api/hub/servers/route_client-reference-manifest.js +3 -0
- package/.next/server/app/index.html +2 -2
- package/.next/server/app/index.rsc +14 -14
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/index.segments/_full.segment.rsc +14 -14
- package/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/server/app/index.segments/_index.segment.rsc +7 -7
- package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/login.html +2 -2
- package/.next/server/app/login.rsc +14 -14
- package/.next/server/app/login.segments/_full.segment.rsc +14 -14
- package/.next/server/app/login.segments/_head.segment.rsc +4 -4
- package/.next/server/app/login.segments/_index.segment.rsc +7 -7
- package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/login.segments/login.segment.rsc +3 -3
- package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/logs.html +2 -2
- package/.next/server/app/logs.rsc +14 -14
- package/.next/server/app/logs.segments/_full.segment.rsc +14 -14
- package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/logs.segments/_index.segment.rsc +7 -7
- package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
- package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
- package/.next/server/app/messages.html +2 -2
- package/.next/server/app/messages.rsc +14 -14
- package/.next/server/app/messages.segments/_full.segment.rsc +14 -14
- package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
- package/.next/server/app/messages.segments/_index.segment.rsc +7 -7
- package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
- package/.next/server/app/node/page_client-reference-manifest.js +1 -1
- package/.next/server/app/node.html +2 -2
- package/.next/server/app/node.rsc +14 -14
- package/.next/server/app/node.segments/_full.segment.rsc +14 -14
- package/.next/server/app/node.segments/_head.segment.rsc +4 -4
- package/.next/server/app/node.segments/_index.segment.rsc +7 -7
- package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/node.segments/node.segment.rsc +3 -3
- package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
- package/.next/server/app/nodes.html +2 -2
- package/.next/server/app/nodes.rsc +14 -14
- package/.next/server/app/nodes.segments/_full.segment.rsc +14 -14
- package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
- package/.next/server/app/nodes.segments/_index.segment.rsc +7 -7
- package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/server-logs.html +2 -2
- package/.next/server/app/server-logs.rsc +14 -14
- package/.next/server/app/server-logs.segments/_full.segment.rsc +14 -14
- package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/server-logs.segments/_index.segment.rsc +7 -7
- package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
- package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/networks.html +2 -2
- package/.next/server/app/settings/networks.rsc +14 -14
- package/.next/server/app/settings/networks.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/networks.segments/_index.segment.rsc +7 -7
- package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
- package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
- package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens.html +2 -2
- package/.next/server/app/settings/tokens.rsc +14 -14
- package/.next/server/app/settings/tokens.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/tokens.segments/_index.segment.rsc +7 -7
- package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
- package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
- package/.next/server/app/settings.html +2 -2
- package/.next/server/app/settings.rsc +14 -14
- package/.next/server/app/settings.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings.segments/_index.segment.rsc +7 -7
- package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
- package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
- package/.next/server/app/tasks.html +2 -2
- package/.next/server/app/tasks.rsc +14 -14
- package/.next/server/app/tasks.segments/_full.segment.rsc +14 -14
- package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/tasks.segments/_index.segment.rsc +7 -7
- package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
- package/.next/server/app-paths-manifest.json +1 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_servers_route_actions_08-m-cs.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_servers_route_actions_08-m-cs.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js.map +1 -0
- 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/0ia0~8uq23xxr.js +1 -0
- package/.next/static/chunks/0otgjhpse~amj.css +2 -0
- package/.next/static/chunks/0rxrez_cjn.eh.js +1 -0
- package/.next/static/chunks/0tfbneqpxn9gw.js +1 -0
- package/.next/static/chunks/0x8x.29hkju5a.js +1 -0
- package/.next/static/chunks/174re5ar~kxne.js +1 -0
- package/.next/static/chunks/17limi7in00fo.js +4 -0
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/.next/types/routes.d.ts +2 -1
- package/.next/types/validator.ts +9 -0
- package/app/api/hub/servers/route.ts +39 -0
- package/app/components/ChatPopover.tsx +38 -2
- package/app/components/CommandPalette.tsx +107 -0
- package/app/components/HelpOverlay.tsx +126 -0
- package/app/components/ServersDrawer.tsx +64 -27
- package/app/components/TopoGraph.tsx +3569 -255
- package/app/components/utils.ts +8 -7
- package/app/globals.css +75 -5
- package/app/lib/time.ts +36 -0
- package/package.json +1 -1
- package/scripts/help-overlay-topo-test.mjs +44 -0
- package/scripts/servers-drawer-live-test.mjs +96 -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-test.mjs +73 -0
- package/scripts/topo-active-links-freshness-test.mjs +113 -0
- package/scripts/topo-active-links-tooltip-test.mjs +79 -0
- package/scripts/topo-arrival-ping-test.mjs +87 -0
- package/scripts/topo-arrow-tier-test.mjs +61 -0
- package/scripts/topo-auto-fit-test.mjs +73 -0
- package/scripts/topo-canvas-aria-test.mjs +108 -0
- package/scripts/topo-chat-focus-ring-test.mjs +77 -0
- package/scripts/topo-chat-target-breath-test.mjs +84 -0
- package/scripts/topo-chip-focus-test.mjs +95 -0
- package/scripts/topo-chip-pin-mirror-test.mjs +113 -0
- package/scripts/topo-chrome-a11y-test.mjs +107 -0
- package/scripts/topo-click-ripple-test.mjs +66 -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-cwd-tooltip-test.mjs +64 -0
- package/scripts/topo-dblclick-reset-test.mjs +74 -0
- package/scripts/topo-discrete-zoom-smooth-test.mjs +125 -0
- package/scripts/topo-dispatch-pulse-test.mjs +83 -0
- package/scripts/topo-edge-badge-click-pin-test.mjs +94 -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-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-stroke-transition-test.mjs +132 -0
- package/scripts/topo-edge-tooltip-test.mjs +70 -0
- package/scripts/topo-endpoint-ring-test.mjs +93 -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-group-box-pinned-state-test.mjs +95 -0
- package/scripts/topo-group-count-test.mjs +68 -0
- package/scripts/topo-group-hover-test.mjs +82 -0
- package/scripts/topo-group-label-a11y-test.mjs +113 -0
- package/scripts/topo-group-label-hover-test.mjs +119 -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-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-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-hub-a11y-test.mjs +112 -0
- package/scripts/topo-hub-busyness-test.mjs +100 -0
- package/scripts/topo-hub-click-fit-test.mjs +137 -0
- package/scripts/topo-hub-hover-ring-test.mjs +82 -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-label-lift-test.mjs +119 -0
- package/scripts/topo-label-scale-test.mjs +78 -0
- package/scripts/topo-lastseen-tooltip-test.mjs +59 -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-test.mjs +131 -0
- package/scripts/topo-legend-count-test.mjs +88 -0
- package/scripts/topo-legend-hover-test.mjs +113 -0
- package/scripts/topo-legend-panel-header-test.mjs +83 -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-tint-test.mjs +95 -0
- package/scripts/topo-legend-row-title-test.mjs +102 -0
- package/scripts/topo-minimap-a11y-test.mjs +101 -0
- package/scripts/topo-minimap-test.mjs +68 -0
- package/scripts/topo-mobile-chips-test.mjs +103 -0
- package/scripts/topo-node-a11y-test.mjs +107 -0
- package/scripts/topo-node-hover-lift-test.mjs +85 -0
- package/scripts/topo-node-stroke-transition-test.mjs +103 -0
- package/scripts/topo-node-title-peers-test.mjs +107 -0
- package/scripts/topo-node-title-test.mjs +101 -0
- package/scripts/topo-nodesize-crossfade-test.mjs +141 -0
- package/scripts/topo-offline-dim-test.mjs +70 -0
- package/scripts/topo-online-chip-nav-test.mjs +143 -0
- package/scripts/topo-orbit-busyness-test.mjs +96 -0
- package/scripts/topo-overlap-test.mjs +32 -5
- package/scripts/topo-pan-cursor-test.mjs +62 -0
- package/scripts/topo-panel-elevation-test.mjs +76 -0
- package/scripts/topo-panel-hover-elevation-test.mjs +118 -0
- package/scripts/topo-particle-stagger-test.mjs +82 -0
- package/scripts/topo-pin-intersection-empty-test.mjs +129 -0
- package/scripts/topo-pin-intersection-test.mjs +121 -0
- package/scripts/topo-pin-persistence-test.mjs +105 -0
- package/scripts/topo-popover-meta-test.mjs +78 -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-tooltip-test.mjs +98 -0
- package/scripts/topo-pressure-seg-transition-test.mjs +107 -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-hot-header-test.mjs +132 -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-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-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-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-spoke-busyness-test.mjs +110 -0
- package/scripts/topo-status-chip-hover-test.mjs +101 -0
- package/scripts/topo-status-chip-tooltip-test.mjs +95 -0
- package/scripts/topo-svg-focus-test.mjs +112 -0
- package/scripts/topo-sweep-busyness-test.mjs +94 -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-tz-ghost-test.mjs +69 -0
- package/scripts/topo-vendor-chip-hover-test.mjs +111 -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-wheel-zoom-test.mjs +79 -0
- package/scripts/topo-working-chip-pin-test.mjs +148 -0
- package/scripts/topo-working-halo-breath-test.mjs +86 -0
- package/scripts/topo-zoom-keys-test.mjs +89 -0
- package/.next/static/chunks/0-7iowzu4.798.js +0 -1
- package/.next/static/chunks/0-elsllb_uimz.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/0ang8z1ph~8nf.js +0 -1
- package/.next/static/chunks/0elalb9n_.~di.js +0 -1
- package/.next/static/chunks/0fqy_0wj_1xa7.js +0 -7
- package/.next/static/chunks/0j_q__o8p6g83.js +0 -1
- package/.next/static/chunks/0jl6_w50zh4l5.js +0 -1
- package/.next/static/chunks/0n_cardir-ztl.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/{x222cMNV4lquOMK8fPD3G → Bfq_NKAKfgisiPZVimIBB}/_buildManifest.js +0 -0
- /package/.next/static/{x222cMNV4lquOMK8fPD3G → Bfq_NKAKfgisiPZVimIBB}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{x222cMNV4lquOMK8fPD3G → Bfq_NKAKfgisiPZVimIBB}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/* Round 119 verification: when a recent-signal row is pinned (R116),
|
|
2
|
+
* the chip row renders a 4th "filter: from→to · N" pill matching
|
|
3
|
+
* the R64 / R89 pattern for status / group / vendor pins. Closes the
|
|
4
|
+
* pill-pattern gap left after R116.
|
|
5
|
+
*
|
|
6
|
+
* Verifies:
|
|
7
|
+
* - Before pin: no edge pill
|
|
8
|
+
* - Click row → pill shows correct from→to and count
|
|
9
|
+
* - Click pill body → clears
|
|
10
|
+
* - × button also clears
|
|
11
|
+
* - data-filter-match-aliases lists the two endpoints
|
|
12
|
+
*/
|
|
13
|
+
import { chromium } from 'playwright';
|
|
14
|
+
import { readFileSync } from 'node:fs';
|
|
15
|
+
|
|
16
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
17
|
+
const browser = await chromium.launch({ headless: true });
|
|
18
|
+
|
|
19
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
20
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
21
|
+
await ctx.addInitScript(() => {
|
|
22
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
26
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
27
|
+
const r = await route.fetch();
|
|
28
|
+
const b = await r.json();
|
|
29
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
30
|
+
const mk = (alias) => ({
|
|
31
|
+
alias, status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
32
|
+
network_id: nid, project_dir: null,
|
|
33
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
34
|
+
});
|
|
35
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta'), mk('gamma')] } });
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const now = Date.now();
|
|
39
|
+
const mkMsg = (id, from_alias, to_alias, ageMs) => ({
|
|
40
|
+
id, from_alias, to_alias, content: 'hi', network_id: 'default',
|
|
41
|
+
created_at: new Date(now - ageMs).toISOString(),
|
|
42
|
+
});
|
|
43
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
|
|
44
|
+
mkMsg('m1', 'alpha', 'beta', 30 * 1000),
|
|
45
|
+
mkMsg('m2', 'alpha', 'beta', 40 * 1000),
|
|
46
|
+
mkMsg('m3', 'alpha', 'beta', 50 * 1000),
|
|
47
|
+
] } }));
|
|
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 === 3, { timeout: 30000 });
|
|
52
|
+
await page.waitForSelector('[data-recent-row]', { timeout: 10000 });
|
|
53
|
+
await page.waitForTimeout(400);
|
|
54
|
+
|
|
55
|
+
const readPill = () => page.evaluate(() => {
|
|
56
|
+
const p = document.querySelector('[data-active-filter="edge"]');
|
|
57
|
+
if (!p) return null;
|
|
58
|
+
return {
|
|
59
|
+
text: (p.textContent || '').trim(),
|
|
60
|
+
count: p.getAttribute('data-filter-match-count'),
|
|
61
|
+
aliases: p.getAttribute('data-filter-match-aliases') || '',
|
|
62
|
+
title: p.getAttribute('title') || '',
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const before = await readPill();
|
|
67
|
+
|
|
68
|
+
// Click row to pin.
|
|
69
|
+
await page.locator('[data-recent-row="alpha->beta"]').click();
|
|
70
|
+
await page.mouse.move(10, 10);
|
|
71
|
+
await page.waitForTimeout(300);
|
|
72
|
+
const afterPin = await readPill();
|
|
73
|
+
|
|
74
|
+
// Click pill body to clear.
|
|
75
|
+
await page.locator('[data-active-filter="edge"]').click();
|
|
76
|
+
await page.mouse.move(10, 10);
|
|
77
|
+
await page.waitForTimeout(300);
|
|
78
|
+
const afterBodyClear = await readPill();
|
|
79
|
+
|
|
80
|
+
// Pin again, then click × button.
|
|
81
|
+
await page.locator('[data-recent-row="alpha->beta"]').click();
|
|
82
|
+
await page.mouse.move(10, 10);
|
|
83
|
+
await page.waitForTimeout(300);
|
|
84
|
+
await page.locator('[data-active-filter="edge"] button').click();
|
|
85
|
+
await page.waitForTimeout(300);
|
|
86
|
+
const afterXClear = await readPill();
|
|
87
|
+
|
|
88
|
+
await browser.close();
|
|
89
|
+
|
|
90
|
+
const results = {
|
|
91
|
+
before_noPill: before === null,
|
|
92
|
+
pin_pillRendered: afterPin !== null,
|
|
93
|
+
pin_textContainsArrow: /alpha→beta/.test(afterPin?.text || ''),
|
|
94
|
+
pin_count3: afterPin?.count === '3',
|
|
95
|
+
pin_aliasesMatch: afterPin?.aliases === 'alpha,beta',
|
|
96
|
+
pin_titleHasFromTo: /alpha → beta \(3 msgs\)/.test(afterPin?.title || ''),
|
|
97
|
+
bodyClear_pillGone: afterBodyClear === null,
|
|
98
|
+
xClear_pillGone: afterXClear === null,
|
|
99
|
+
};
|
|
100
|
+
const ok = Object.values(results).every(Boolean);
|
|
101
|
+
console.log(`${ok ? '✅' : '❌'} edge pill:`, JSON.stringify(results),
|
|
102
|
+
`\n afterPin=`, afterPin,
|
|
103
|
+
`\n afterBodyClear=`, afterBodyClear,
|
|
104
|
+
`\n afterXClear=`, afterXClear);
|
|
105
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/* Round 166 verification: visible flow edge path picks up
|
|
2
|
+
* stroke-width transition.
|
|
3
|
+
*
|
|
4
|
+
* R50 thickens hovered edges to 1.4× (renderWidth = width * 1.4)
|
|
5
|
+
* but pre-R166 only opacity transitioned — width snapped. R166
|
|
6
|
+
* adds stroke-width to the transition list so the edge thickens
|
|
7
|
+
* smoothly on hover, pairing with R164 edge-badge r-lift for
|
|
8
|
+
* coordinated 300ms ease-out across the edge surface.
|
|
9
|
+
*
|
|
10
|
+
* Test:
|
|
11
|
+
* 1. Mock 4 sessions + 1 flow alpha→beta
|
|
12
|
+
* 2. Probe visible path before hover → strokeWidth=base
|
|
13
|
+
* 3. Probe transition string includes both opacity and
|
|
14
|
+
* stroke-width
|
|
15
|
+
* 4. Hover edge hitbox → wait 350ms (past transition)
|
|
16
|
+
* 5. Probe strokeWidth → should be ≈ base * 1.4
|
|
17
|
+
* 6. mouse away → strokeWidth back to base
|
|
18
|
+
*/
|
|
19
|
+
import { chromium } from 'playwright';
|
|
20
|
+
import { readFileSync } from 'node:fs';
|
|
21
|
+
|
|
22
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
23
|
+
const browser = await chromium.launch({ headless: true });
|
|
24
|
+
|
|
25
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
26
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
27
|
+
await ctx.addInitScript(() => {
|
|
28
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
32
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
33
|
+
const r = await route.fetch();
|
|
34
|
+
const b = await r.json();
|
|
35
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
36
|
+
const mk = (alias) => ({
|
|
37
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
38
|
+
network_id: nid, project_dir: null,
|
|
39
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
40
|
+
});
|
|
41
|
+
// 4 sessions so alpha + beta are 90° apart (not 180° — that
|
|
42
|
+
// would put their edge midpoint on the hub, where the hub
|
|
43
|
+
// <g> intercepts pointer events). gamma + delta are filler.
|
|
44
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
45
|
+
mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
|
|
46
|
+
] } });
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const now = Date.now();
|
|
50
|
+
// 4 msgs so:
|
|
51
|
+
// - width = Math.min(2+4, 7) = 6
|
|
52
|
+
// - hover renderWidth = Math.min(6*1.4, 10) = 8.4
|
|
53
|
+
// AND the edge midpoint count badge renders (R100 threshold
|
|
54
|
+
// count >= 3) so we can use it as the hover surface. The
|
|
55
|
+
// visible path has pointerEvents:none; the R48 hitbox is a
|
|
56
|
+
// thin stroke that Playwright's .hover() can't reliably land
|
|
57
|
+
// on. The badge is a r=9 circle with pointerEvents:all and
|
|
58
|
+
// propagates hover to hoveredEdgeKey (R122) — most reliable
|
|
59
|
+
// target for triggering the visible-path transition.
|
|
60
|
+
const msgs = [];
|
|
61
|
+
for (let i = 0; i < 4; i++) {
|
|
62
|
+
msgs.push({ id: `m${i}`, from_alias: 'alpha', to_alias: 'beta', content: 'hi',
|
|
63
|
+
network_id: 'default', created_at: new Date(now - (10000 + i * 500)).toISOString() });
|
|
64
|
+
}
|
|
65
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
|
|
66
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
67
|
+
|
|
68
|
+
const page = await ctx.newPage();
|
|
69
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
70
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
71
|
+
await page.waitForSelector('[data-edge-visible]', { timeout: 10000 });
|
|
72
|
+
await page.waitForTimeout(400);
|
|
73
|
+
|
|
74
|
+
const idle = await page.evaluate(() => {
|
|
75
|
+
const el = document.querySelector('[data-edge-visible]');
|
|
76
|
+
if (!el) return null;
|
|
77
|
+
return {
|
|
78
|
+
strokeWidth: parseFloat(el.getAttribute('stroke-width')),
|
|
79
|
+
inlineTransition: el.style.transition || '',
|
|
80
|
+
computedTransition: getComputedStyle(el).transition || '',
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Hover the edge midpoint badge (R164 r=9 circle, pointerEvents:all,
|
|
85
|
+
// propagates to hoveredEdgeKey via R122). The visible path has
|
|
86
|
+
// pointerEvents:none; the R48 hitbox is a thin stroke that
|
|
87
|
+
// Playwright .hover() can't reliably land on.
|
|
88
|
+
await page.locator('[data-edge-count-badge]').hover();
|
|
89
|
+
// Wait for transition to settle.
|
|
90
|
+
await page.waitForTimeout(400);
|
|
91
|
+
const hovered = await page.evaluate(() => {
|
|
92
|
+
const el = document.querySelector('[data-edge-visible]');
|
|
93
|
+
if (!el) return null;
|
|
94
|
+
return {
|
|
95
|
+
strokeWidth: parseFloat(el.getAttribute('stroke-width')),
|
|
96
|
+
};
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Move cursor away
|
|
100
|
+
await page.mouse.move(10, 10);
|
|
101
|
+
await page.waitForTimeout(400);
|
|
102
|
+
const idleAgain = await page.evaluate(() => {
|
|
103
|
+
const el = document.querySelector('[data-edge-visible]');
|
|
104
|
+
if (!el) return null;
|
|
105
|
+
return {
|
|
106
|
+
strokeWidth: parseFloat(el.getAttribute('stroke-width')),
|
|
107
|
+
};
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
await browser.close();
|
|
111
|
+
|
|
112
|
+
const hasOpacity = (idle?.inlineTransition || '').includes('opacity 300ms') ||
|
|
113
|
+
/opacity\s+0\.3s|opacity\s+300ms/.test(idle?.computedTransition || '');
|
|
114
|
+
const hasStrokeWidth = (idle?.inlineTransition || '').includes('stroke-width 300ms') ||
|
|
115
|
+
/stroke-width\s+0\.3s|stroke-width\s+300ms/.test(idle?.computedTransition || '');
|
|
116
|
+
|
|
117
|
+
// Expected: width=Math.min(2+4, 7)=6. Hover renderWidth=Math.min(6*1.4, 10)=8.4.
|
|
118
|
+
const results = {
|
|
119
|
+
edge_found: idle !== null,
|
|
120
|
+
has_opacity_transition: hasOpacity,
|
|
121
|
+
has_stroke_width_transition: hasStrokeWidth,
|
|
122
|
+
idle_width_6: idle && Math.abs(idle.strokeWidth - 6) < 0.05,
|
|
123
|
+
hover_width_8p4: hovered && Math.abs(hovered.strokeWidth - 8.4) < 0.1,
|
|
124
|
+
hover_width_thicker: hovered && idle && hovered.strokeWidth > idle.strokeWidth,
|
|
125
|
+
idle_again_width_6: idleAgain && Math.abs(idleAgain.strokeWidth - 6) < 0.05,
|
|
126
|
+
};
|
|
127
|
+
const ok = Object.values(results).every(Boolean);
|
|
128
|
+
console.log(`${ok ? '✅' : '❌'} edge stroke transition:`, JSON.stringify(results),
|
|
129
|
+
`\n idle =`, idle,
|
|
130
|
+
`\n hovered =`, hovered,
|
|
131
|
+
`\n idleAgain =`, idleAgain);
|
|
132
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/* Round 39 verification: each flow-link path carries a `<title>` with
|
|
2
|
+
* "from → to\nN messages · last <relative>". Tests:
|
|
3
|
+
* - tooltip text shape (route + count + relative time)
|
|
4
|
+
* - singular vs. plural ("1 message" vs "5 messages")
|
|
5
|
+
* - missing last_at falls through cleanly */
|
|
6
|
+
import { chromium } from 'playwright';
|
|
7
|
+
import { readFileSync } from 'node:fs';
|
|
8
|
+
|
|
9
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
10
|
+
const browser = await chromium.launch({ headless: true });
|
|
11
|
+
|
|
12
|
+
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
|
13
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
14
|
+
await ctx.addInitScript(() => {
|
|
15
|
+
try {
|
|
16
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
17
|
+
localStorage.setItem('anet-topo-layout', 'grid');
|
|
18
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
19
|
+
} catch {}
|
|
20
|
+
});
|
|
21
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
22
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
23
|
+
const r = await route.fetch();
|
|
24
|
+
const b = await r.json();
|
|
25
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
26
|
+
const sessions = ['alpha', 'beta', 'gamma'].map(a => ({
|
|
27
|
+
alias: a, status: 'idle', network_id: nid, project_dir: null,
|
|
28
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
29
|
+
}));
|
|
30
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
31
|
+
});
|
|
32
|
+
// Inject messages: 1 between alpha→beta (singular), 5 between alpha→gamma (plural).
|
|
33
|
+
const old = new Date(Date.now() - 8 * 60 * 1000).toISOString();
|
|
34
|
+
const recent = new Date(Date.now() - 30 * 1000).toISOString();
|
|
35
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
|
|
36
|
+
{ from_alias: 'alpha', to_alias: 'beta', content: 'hello', created_at: old },
|
|
37
|
+
{ from_alias: 'alpha', to_alias: 'gamma', content: 'm1', created_at: recent },
|
|
38
|
+
{ from_alias: 'alpha', to_alias: 'gamma', content: 'm2', created_at: recent },
|
|
39
|
+
{ from_alias: 'alpha', to_alias: 'gamma', content: 'm3', created_at: recent },
|
|
40
|
+
{ from_alias: 'alpha', to_alias: 'gamma', content: 'm4', created_at: recent },
|
|
41
|
+
{ from_alias: 'alpha', to_alias: 'gamma', content: 'm5', created_at: recent },
|
|
42
|
+
] } }));
|
|
43
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
44
|
+
const page = await ctx.newPage();
|
|
45
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
46
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
|
|
47
|
+
await page.waitForTimeout(600);
|
|
48
|
+
|
|
49
|
+
// Collect every flow-link tooltip (each base path with a child <title>).
|
|
50
|
+
const tooltips = await page.evaluate(() => {
|
|
51
|
+
const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
|
|
52
|
+
// Two paths per flow link — the base one carries the <title>.
|
|
53
|
+
const titled = [...svg.querySelectorAll('path > title')];
|
|
54
|
+
return titled.map(t => t.textContent);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
await browser.close();
|
|
58
|
+
const has = (route, count, plural) => tooltips.some(t =>
|
|
59
|
+
t.includes(route) &&
|
|
60
|
+
(plural ? t.includes(`${count} messages`) : t.includes(`${count} message`)) &&
|
|
61
|
+
/last \w+ ago/.test(t)
|
|
62
|
+
);
|
|
63
|
+
const results = {
|
|
64
|
+
singularAlphaToBeta: has('alpha → beta', 1, false),
|
|
65
|
+
pluralAlphaToGamma: has('alpha → gamma', 5, true),
|
|
66
|
+
tooltipCount: tooltips.length === 2,
|
|
67
|
+
};
|
|
68
|
+
const ok = Object.values(results).every(Boolean);
|
|
69
|
+
console.log(`${ok ? '✅' : '❌'} edge tooltip:`, JSON.stringify(results), '\n tooltips=', tooltips);
|
|
70
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/* Round 111 verification: when an edge is hovered, BOTH endpoint
|
|
2
|
+
* nodes get an accent stroke ring (data-edge-endpoint-ring) while
|
|
3
|
+
* non-endpoint nodes don't. R49 already kept endpoints at opacity 1
|
|
4
|
+
* but offered no positive cue; the ring closes that.
|
|
5
|
+
*
|
|
6
|
+
* Fleet: alpha → beta with 4 messages (sole flow), gamma idle (no
|
|
7
|
+
* flows). Hover the edge → alpha + beta each carry a ring; gamma
|
|
8
|
+
* carries none. Mouseleave → all rings vanish.
|
|
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
|
+
|
|
16
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
17
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
18
|
+
await ctx.addInitScript(() => {
|
|
19
|
+
try {
|
|
20
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
21
|
+
localStorage.setItem('anet-topo-layout', 'ring');
|
|
22
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
23
|
+
} catch {}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
27
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
28
|
+
const r = await route.fetch();
|
|
29
|
+
const b = await r.json();
|
|
30
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
31
|
+
const mk = (alias) => ({
|
|
32
|
+
alias, status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
33
|
+
network_id: nid, project_dir: null,
|
|
34
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
35
|
+
});
|
|
36
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta'), mk('gamma')] } });
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const now = Date.now();
|
|
40
|
+
const mkMsg = (id, from_alias, to_alias, ageMs) => ({
|
|
41
|
+
id, from_alias, to_alias, content: 'hi', network_id: 'default',
|
|
42
|
+
created_at: new Date(now - ageMs).toISOString(),
|
|
43
|
+
});
|
|
44
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
|
|
45
|
+
mkMsg('m1', 'alpha', 'beta', 30 * 1000),
|
|
46
|
+
mkMsg('m2', 'alpha', 'beta', 40 * 1000),
|
|
47
|
+
mkMsg('m3', 'alpha', 'beta', 50 * 1000),
|
|
48
|
+
mkMsg('m4', 'alpha', 'beta', 60 * 1000),
|
|
49
|
+
] } }));
|
|
50
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
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 === 3, { timeout: 30000 });
|
|
54
|
+
await page.waitForTimeout(400);
|
|
55
|
+
|
|
56
|
+
const ringByAlias = () => page.evaluate(() => {
|
|
57
|
+
const out = {};
|
|
58
|
+
for (const alias of ['alpha', 'beta', 'gamma']) {
|
|
59
|
+
const g = document.querySelector(`g[data-node="${alias}"]`);
|
|
60
|
+
out[alias] = !!g?.querySelector('[data-edge-endpoint-ring]');
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const before = await ringByAlias();
|
|
66
|
+
|
|
67
|
+
// Hover the edge hitbox (the invisible 16-px stroke that owns the
|
|
68
|
+
// hover surface per R48). The recent-signal row provides a stable
|
|
69
|
+
// way to set hoveredEdgeKey without finding the path on the canvas.
|
|
70
|
+
await page.locator('[data-recent-row="alpha->beta"]').hover();
|
|
71
|
+
await page.waitForTimeout(300);
|
|
72
|
+
const onHover = await ringByAlias();
|
|
73
|
+
|
|
74
|
+
// Move away.
|
|
75
|
+
await page.mouse.move(10, 10);
|
|
76
|
+
await page.waitForTimeout(300);
|
|
77
|
+
const afterLeave = await ringByAlias();
|
|
78
|
+
|
|
79
|
+
await browser.close();
|
|
80
|
+
|
|
81
|
+
const results = {
|
|
82
|
+
before_noneHaveRing: !before.alpha && !before.beta && !before.gamma,
|
|
83
|
+
hover_alphaRing: onHover.alpha === true,
|
|
84
|
+
hover_betaRing: onHover.beta === true,
|
|
85
|
+
hover_gammaNoRing: onHover.gamma === false,
|
|
86
|
+
leave_allClear: !afterLeave.alpha && !afterLeave.beta && !afterLeave.gamma,
|
|
87
|
+
};
|
|
88
|
+
const ok = Object.values(results).every(Boolean);
|
|
89
|
+
console.log(`${ok ? '✅' : '❌'} edge-endpoint ring:`, JSON.stringify(results),
|
|
90
|
+
`\n before=`, before,
|
|
91
|
+
`\n onHover=`, onHover,
|
|
92
|
+
`\n afterLeave=`, afterLeave);
|
|
93
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/* Round 62 verification: pressing Esc clears pinnedStatus when no chat
|
|
2
|
+
* is open. Composes cleanly with the R136 ChatPopover Esc handler
|
|
3
|
+
* (which only mounts while chatAlias is set).
|
|
4
|
+
*
|
|
5
|
+
* - Pin via legend row (R61) → assert filter active.
|
|
6
|
+
* - Press Esc → assert pin released, all nodes restore.
|
|
7
|
+
* - aria-pressed flips back to false.
|
|
8
|
+
*/
|
|
9
|
+
import { chromium } from 'playwright';
|
|
10
|
+
import { readFileSync } from 'node:fs';
|
|
11
|
+
|
|
12
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
13
|
+
const browser = await chromium.launch({ headless: true });
|
|
14
|
+
|
|
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
|
+
} catch {}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
26
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
27
|
+
const r = await route.fetch();
|
|
28
|
+
const b = await r.json();
|
|
29
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
30
|
+
const sessions = [
|
|
31
|
+
{ alias: 'wkr', status: 'working', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
32
|
+
{ alias: 'idl1', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
33
|
+
{ alias: 'idl2', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
34
|
+
];
|
|
35
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
36
|
+
});
|
|
37
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
38
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
39
|
+
const page = await ctx.newPage();
|
|
40
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
41
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
|
|
42
|
+
await page.waitForTimeout(500);
|
|
43
|
+
|
|
44
|
+
const opacities = () => page.evaluate(() => {
|
|
45
|
+
const o = {};
|
|
46
|
+
for (const a of ['wkr', 'idl1', 'idl2']) {
|
|
47
|
+
const g = document.querySelector(`g[data-node="${a}"]`);
|
|
48
|
+
o[a] = g ? +(g.style.opacity || '1') : null;
|
|
49
|
+
}
|
|
50
|
+
return o;
|
|
51
|
+
});
|
|
52
|
+
const ariaPressed = (status) => page.evaluate(s => {
|
|
53
|
+
const g = document.querySelector(`g[data-legend-status="${s}"]`);
|
|
54
|
+
return g?.getAttribute('aria-pressed');
|
|
55
|
+
}, status);
|
|
56
|
+
|
|
57
|
+
const before = await opacities();
|
|
58
|
+
|
|
59
|
+
// Pin working via legend row click.
|
|
60
|
+
await page.locator('g[data-legend-status="working"]').first().click({ force: true });
|
|
61
|
+
// Move mouse away so we observe pin-only (not hover-driven) filter.
|
|
62
|
+
await page.mouse.move(10, 10);
|
|
63
|
+
await page.waitForTimeout(250);
|
|
64
|
+
const afterPin = await opacities();
|
|
65
|
+
const ariaAfterPin = await ariaPressed('working');
|
|
66
|
+
|
|
67
|
+
// Press Esc. Body should have focus so the document-level keydown
|
|
68
|
+
// handler fires.
|
|
69
|
+
await page.evaluate(() => document.activeElement && typeof document.activeElement.blur === 'function' && document.activeElement.blur());
|
|
70
|
+
await page.keyboard.press('Escape');
|
|
71
|
+
await page.waitForTimeout(250);
|
|
72
|
+
const afterEsc = await opacities();
|
|
73
|
+
const ariaAfterEsc = await ariaPressed('working');
|
|
74
|
+
|
|
75
|
+
// Press Esc again — should be a no-op (no pin, no chat).
|
|
76
|
+
await page.keyboard.press('Escape');
|
|
77
|
+
await page.waitForTimeout(150);
|
|
78
|
+
const afterEsc2 = await opacities();
|
|
79
|
+
|
|
80
|
+
await browser.close();
|
|
81
|
+
|
|
82
|
+
const bright = (v) => v != null && v >= 0.55;
|
|
83
|
+
const dim = (v) => v != null && v < 0.4;
|
|
84
|
+
const results = {
|
|
85
|
+
before_baseline: bright(before.wkr) && bright(before.idl1) && bright(before.idl2),
|
|
86
|
+
pin_keepsWorking: bright(afterPin.wkr),
|
|
87
|
+
pin_dimsIdle: dim(afterPin.idl1) && dim(afterPin.idl2),
|
|
88
|
+
pin_ariaTrue: ariaAfterPin === 'true',
|
|
89
|
+
esc_releasesPin: bright(afterEsc.wkr) && bright(afterEsc.idl1) && bright(afterEsc.idl2),
|
|
90
|
+
esc_ariaBackToFalse: ariaAfterEsc === 'false',
|
|
91
|
+
escAgain_isNoOp: bright(afterEsc2.wkr) && bright(afterEsc2.idl1),
|
|
92
|
+
};
|
|
93
|
+
const ok = Object.values(results).every(Boolean);
|
|
94
|
+
console.log(`${ok ? '✅' : '❌'} esc clears pin:`, JSON.stringify(results),
|
|
95
|
+
`\n before=`, before,
|
|
96
|
+
`\n afterPin=`, afterPin, ` aria=${ariaAfterPin}`,
|
|
97
|
+
`\n afterEsc=`, afterEsc, ` aria=${ariaAfterEsc}`);
|
|
98
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/* Round 97 verification: each active-filter pill carries the matched
|
|
2
|
+
* alias list in its title attribute + data-filter-match-aliases for
|
|
3
|
+
* tests. Pre-R97 the pill said "filter: working · 3" but you had to
|
|
4
|
+
* eyeball the dimmed canvas to find which 3 nodes were the matches.
|
|
5
|
+
*
|
|
6
|
+
* Drive each pin type and read the alias data attr. Fleet has known
|
|
7
|
+
* mix so the expected alias sets are deterministic.
|
|
8
|
+
*
|
|
9
|
+
* Truncation at 8 aliases is tested via a fleet with > 8 working
|
|
10
|
+
* nodes — confirms the data attr stores the full list (for test
|
|
11
|
+
* stability) while the visible title shows a "+N more" suffix.
|
|
12
|
+
*/
|
|
13
|
+
import { chromium } from 'playwright';
|
|
14
|
+
import { readFileSync } from 'node:fs';
|
|
15
|
+
|
|
16
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
17
|
+
const browser = await chromium.launch({ headless: true });
|
|
18
|
+
|
|
19
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
20
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
21
|
+
await ctx.addInitScript(() => {
|
|
22
|
+
try {
|
|
23
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
24
|
+
localStorage.setItem('anet-topo-layout', 'grid');
|
|
25
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
26
|
+
sessionStorage.removeItem('anet-topo-pinned-status');
|
|
27
|
+
sessionStorage.removeItem('anet-topo-pinned-group');
|
|
28
|
+
sessionStorage.removeItem('anet-topo-pinned-vendor');
|
|
29
|
+
} 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, status, model) => ({
|
|
38
|
+
alias, status, model, runtime: 'cli-claude-code',
|
|
39
|
+
network_id: nid, project_dir: null,
|
|
40
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
41
|
+
});
|
|
42
|
+
// Fleet: 3 working (alpha1/2 + beta1), 3 idle (alpha3 + beta2 + gamma1),
|
|
43
|
+
// 2 vendors A/O. alpha-prefix = 3 members, beta-prefix = 2, gamma = 1.
|
|
44
|
+
const sessions = [
|
|
45
|
+
mk('alpha1', 'working', 'claude-opus-4'),
|
|
46
|
+
mk('alpha2', 'working', 'claude-opus-4'),
|
|
47
|
+
mk('alpha3', 'idle', 'claude-opus-4'),
|
|
48
|
+
mk('beta1', 'working', 'gpt-5'),
|
|
49
|
+
mk('beta2', 'idle', 'gpt-5'),
|
|
50
|
+
mk('gamma1', 'idle', 'claude-opus-4'),
|
|
51
|
+
];
|
|
52
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
53
|
+
});
|
|
54
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
55
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
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 === 6, { timeout: 30000 });
|
|
59
|
+
await page.waitForTimeout(400);
|
|
60
|
+
|
|
61
|
+
const readPill = (kind) => page.evaluate((k) => {
|
|
62
|
+
const p = document.querySelector(`[data-active-filter="${k}"]`);
|
|
63
|
+
if (!p) return null;
|
|
64
|
+
return {
|
|
65
|
+
title: p.getAttribute('title') || '',
|
|
66
|
+
aliases: p.getAttribute('data-filter-match-aliases') || '',
|
|
67
|
+
count: p.getAttribute('data-filter-match-count') || '',
|
|
68
|
+
};
|
|
69
|
+
}, kind);
|
|
70
|
+
|
|
71
|
+
const pin = async (kind, value) => {
|
|
72
|
+
await page.evaluate(({ kind, value }) => {
|
|
73
|
+
if (kind === 'status') sessionStorage.setItem('anet-topo-pinned-status', value);
|
|
74
|
+
if (kind === 'group') sessionStorage.setItem('anet-topo-pinned-group', value);
|
|
75
|
+
if (kind === 'vendor') sessionStorage.setItem('anet-topo-pinned-vendor', value);
|
|
76
|
+
window.dispatchEvent(new CustomEvent('anet:topo-pin', { detail: { kind, value } }));
|
|
77
|
+
}, { kind, value });
|
|
78
|
+
await page.waitForTimeout(200);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// Status pin → working should match alpha1, alpha2, beta1.
|
|
82
|
+
await pin('status', 'working');
|
|
83
|
+
const statusPill = await readPill('status');
|
|
84
|
+
|
|
85
|
+
// Vendor pin → A should match all claude-opus-4 sessions (alphas + gamma).
|
|
86
|
+
await page.locator('[data-vendor-letter="A"]').click();
|
|
87
|
+
await page.mouse.move(10, 10);
|
|
88
|
+
await page.waitForTimeout(250);
|
|
89
|
+
const vendorPill = await readPill('vendor');
|
|
90
|
+
|
|
91
|
+
// Group pin → alpha should match alpha1/2/3.
|
|
92
|
+
await page.evaluate(() => {
|
|
93
|
+
sessionStorage.setItem('anet-topo-pinned-group', 'alpha');
|
|
94
|
+
window.dispatchEvent(new CustomEvent('anet:topo-pin', { detail: { kind: 'group', value: 'alpha' } }));
|
|
95
|
+
});
|
|
96
|
+
await page.waitForTimeout(250);
|
|
97
|
+
const groupPill = await readPill('group');
|
|
98
|
+
|
|
99
|
+
await browser.close();
|
|
100
|
+
|
|
101
|
+
const sameSet = (csv, expected) => {
|
|
102
|
+
const got = new Set(csv.split(',').filter(Boolean));
|
|
103
|
+
const exp = new Set(expected);
|
|
104
|
+
return got.size === exp.size && [...exp].every(x => got.has(x));
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const results = {
|
|
108
|
+
status_aliasesMatch: sameSet(statusPill?.aliases || '', ['alpha1', 'alpha2', 'beta1']),
|
|
109
|
+
status_titleListsThem: /alpha1.*alpha2.*beta1|alpha2.*alpha1.*beta1/.test(statusPill?.title || ''),
|
|
110
|
+
status_titleHasClear: /click to clear/i.test(statusPill?.title || ''),
|
|
111
|
+
group_aliasesMatch: sameSet(groupPill?.aliases || '', ['alpha1', 'alpha2', 'alpha3']),
|
|
112
|
+
group_titleListsThem: /alpha1.*alpha2.*alpha3/.test(groupPill?.title || ''),
|
|
113
|
+
vendor_aliasesMatch: sameSet(vendorPill?.aliases || '', ['alpha1', 'alpha2', 'alpha3', 'gamma1']),
|
|
114
|
+
vendor_titleListsThem: /alpha1.*alpha2.*alpha3.*gamma1/.test(vendorPill?.title || ''),
|
|
115
|
+
};
|
|
116
|
+
const ok = Object.values(results).every(Boolean);
|
|
117
|
+
console.log(`${ok ? '✅' : '❌'} filter pill aliases:`, JSON.stringify(results),
|
|
118
|
+
`\n status=`, statusPill,
|
|
119
|
+
`\n group=`, groupPill,
|
|
120
|
+
`\n vendor=`, vendorPill);
|
|
121
|
+
process.exit(ok ? 0 : 1);
|