@sleep2agi/agent-network-dashboard 0.5.1-preview.5 → 0.5.1-preview.51
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 +6 -6
- package/.next/fallback-build-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 +2 -2
- package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- 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 +2 -2
- package/.next/server/app/admin.segments/_full.segment.rsc +2 -2
- package/.next/server/app/admin.segments/_head.segment.rsc +1 -1
- package/.next/server/app/admin.segments/_index.segment.rsc +2 -2
- package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/admin.segments/admin.segment.rsc +1 -1
- package/.next/server/app/index.html +2 -2
- package/.next/server/app/index.rsc +3 -3
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/index.segments/_full.segment.rsc +3 -3
- package/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/server/app/index.segments/_index.segment.rsc +2 -2
- 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 +3 -3
- package/.next/server/app/login.segments/_full.segment.rsc +3 -3
- package/.next/server/app/login.segments/_head.segment.rsc +1 -1
- package/.next/server/app/login.segments/_index.segment.rsc +2 -2
- package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/login.segments/login.segment.rsc +1 -1
- 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 +2 -2
- package/.next/server/app/logs.segments/_full.segment.rsc +2 -2
- package/.next/server/app/logs.segments/_head.segment.rsc +1 -1
- package/.next/server/app/logs.segments/_index.segment.rsc +2 -2
- package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/logs.segments/logs.segment.rsc +1 -1
- 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 +2 -2
- package/.next/server/app/messages.segments/_full.segment.rsc +2 -2
- package/.next/server/app/messages.segments/_head.segment.rsc +1 -1
- package/.next/server/app/messages.segments/_index.segment.rsc +2 -2
- package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/messages.segments/messages.segment.rsc +1 -1
- 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 +2 -2
- package/.next/server/app/node.segments/_full.segment.rsc +2 -2
- package/.next/server/app/node.segments/_head.segment.rsc +1 -1
- package/.next/server/app/node.segments/_index.segment.rsc +2 -2
- package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/node.segments/node.segment.rsc +1 -1
- 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 +2 -2
- package/.next/server/app/nodes.segments/_full.segment.rsc +2 -2
- package/.next/server/app/nodes.segments/_head.segment.rsc +1 -1
- package/.next/server/app/nodes.segments/_index.segment.rsc +2 -2
- package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/nodes.segments/nodes.segment.rsc +1 -1
- 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 +2 -2
- package/.next/server/app/server-logs.segments/_full.segment.rsc +2 -2
- package/.next/server/app/server-logs.segments/_head.segment.rsc +1 -1
- package/.next/server/app/server-logs.segments/_index.segment.rsc +2 -2
- package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/server-logs.segments/server-logs.segment.rsc +1 -1
- 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 +2 -2
- package/.next/server/app/settings/networks.segments/_full.segment.rsc +2 -2
- package/.next/server/app/settings/networks.segments/_head.segment.rsc +1 -1
- package/.next/server/app/settings/networks.segments/_index.segment.rsc +2 -2
- package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +1 -1
- package/.next/server/app/settings/networks.segments/settings.segment.rsc +1 -1
- 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 +2 -2
- package/.next/server/app/settings/tokens.segments/_full.segment.rsc +2 -2
- package/.next/server/app/settings/tokens.segments/_head.segment.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/_index.segment.rsc +2 -2
- package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/settings.segment.rsc +1 -1
- package/.next/server/app/settings.html +2 -2
- package/.next/server/app/settings.rsc +3 -3
- package/.next/server/app/settings.segments/_full.segment.rsc +3 -3
- package/.next/server/app/settings.segments/_head.segment.rsc +1 -1
- package/.next/server/app/settings.segments/_index.segment.rsc +2 -2
- package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/settings.segments/settings.segment.rsc +1 -1
- 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 +2 -2
- package/.next/server/app/tasks.segments/_full.segment.rsc +2 -2
- package/.next/server/app/tasks.segments/_head.segment.rsc +1 -1
- package/.next/server/app/tasks.segments/_index.segment.rsc +2 -2
- package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/tasks.segments/tasks.segment.rsc +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +3 -3
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
- package/.next/server/middleware-build-manifest.js +3 -3
- package/.next/server/pages/404.html +2 -2
- package/.next/server/pages/500.html +1 -1
- package/.next/static/chunks/0bi-134kq-t5m.js +4 -0
- package/.next/static/chunks/0hwb_h953qm4d.css +2 -0
- package/.next/static/chunks/0sf4jjluqr_2t.js +1 -0
- package/.next/static/chunks/109thlclcb2ed.js +1 -0
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/app/components/TopoGraph.tsx +986 -65
- package/package.json +1 -1
- package/scripts/topo-chip-digit-fontweight-test.mjs +105 -0
- package/scripts/topo-chrome-segmented-radius-test.mjs +100 -0
- package/scripts/topo-edge-badge-fontsize-test.mjs +90 -0
- package/scripts/topo-edge-badge-opacity-test.mjs +80 -0
- package/scripts/topo-edge-badge-stroke-test.mjs +92 -0
- package/scripts/topo-edge-visible-linecap-test.mjs +89 -0
- package/scripts/topo-filter-pill-hover-opacity-test.mjs +110 -0
- package/scripts/topo-filter-pill-x-hover-scale-test.mjs +99 -0
- package/scripts/topo-flow-rail-linecap-test.mjs +79 -0
- package/scripts/topo-freshness-chip-tabular-test.mjs +41 -0
- package/scripts/topo-freshness-floor-lift-test.mjs +92 -0
- package/scripts/topo-freshness-suffix-tabular-test.mjs +88 -0
- package/scripts/topo-fullscreen-icon-hover-scale-test.mjs +91 -0
- package/scripts/topo-group-box-stroke-test.mjs +105 -0
- package/scripts/topo-group-label-count-fontweight-test.mjs +108 -0
- package/scripts/topo-hover-detail-body-fw-test.mjs +101 -0
- package/scripts/topo-hover-detail-model-fw-test.mjs +98 -0
- package/scripts/topo-hover-detail-opacity-test.mjs +98 -0
- package/scripts/topo-hover-detail-rx-test.mjs +81 -0
- package/scripts/topo-hub-digit-fontsize-test.mjs +86 -0
- package/scripts/topo-hub-highlight-opacity-test.mjs +88 -0
- package/scripts/topo-hub-highlight-radius-test.mjs +90 -0
- package/scripts/topo-hub-hover-ring-opacity-test.mjs +96 -0
- package/scripts/topo-hub-hover-ring-stroke-test.mjs +86 -0
- package/scripts/topo-hub-spoke-linecap-test.mjs +80 -0
- package/scripts/topo-layout-toggle-hover-tracking-test.mjs +109 -0
- package/scripts/topo-layout-toggle-radius-test.mjs +87 -0
- package/scripts/topo-legend-label-fontweight-test.mjs +94 -0
- package/scripts/topo-minimap-offline-opacity-test.mjs +90 -0
- package/scripts/topo-minimap-online-radius-test.mjs +85 -0
- package/scripts/topo-minimap-viewport-hover-test.mjs +109 -0
- package/scripts/topo-minimap-viewport-linejoin-test.mjs +75 -0
- package/scripts/topo-more-flows-fontweight-test.mjs +103 -0
- package/scripts/topo-panel-count-letterspacing-test.mjs +89 -0
- package/scripts/topo-panel-rect-opacity-hover-test.mjs +109 -0
- package/scripts/topo-panel-title-hover-letterspacing-test.mjs +97 -0
- package/scripts/topo-pressure-bar-height-test.mjs +92 -0
- package/scripts/topo-pressure-kicker-fontweight-test.mjs +76 -0
- package/scripts/topo-recent-pip-radius-2-test.mjs +72 -0
- package/scripts/topo-recent-pip-radius-test.mjs +76 -0
- package/scripts/topo-recent-row-text-fontweight-test.mjs +90 -0
- package/scripts/topo-reset-hover-rotate-test.mjs +102 -0
- package/scripts/topo-vendor-glyph-fontweight-test.mjs +102 -0
- package/scripts/topo-vendor-letter-hover-scale-test.mjs +129 -0
- package/scripts/topo-zoom-icon-hover-scale-test.mjs +114 -0
- package/scripts/topo-zoom-level-hover-letterspacing-test.mjs +91 -0
- package/.next/static/chunks/020yd2d3i1yew.js +0 -1
- package/.next/static/chunks/0aauz~36q5n2a.css +0 -2
- package/.next/static/chunks/0txa4xkx0-7v-.js +0 -1
- package/.next/static/chunks/0xvj-x25qdu55.js +0 -4
- /package/.next/static/{s_gujwHXFfXWh2_yQC6Gk → KuSssxhFiQYKbTq_1BilM}/_buildManifest.js +0 -0
- /package/.next/static/{s_gujwHXFfXWh2_yQC6Gk → KuSssxhFiQYKbTq_1BilM}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{s_gujwHXFfXWh2_yQC6Gk → KuSssxhFiQYKbTq_1BilM}/_ssgManifest.js +0 -0
|
@@ -194,7 +194,27 @@ function FreshnessChip({ sessions }: { sessions: unknown }) {
|
|
|
194
194
|
// tier (R312-R314 family); the amber bg/text/border still does
|
|
195
195
|
// the warning-state work, the weight just keeps the chip in the
|
|
196
196
|
// same data-typography ladder as its siblings.
|
|
197
|
-
|
|
197
|
+
// Round 377 / Loop: FreshnessChip baseClass picks up `tabular-nums`.
|
|
198
|
+
// The chip-row's last untouched chip joins the R224-R357 broader
|
|
199
|
+
// tabular-nums sweep:
|
|
200
|
+
// R224 edge badge digit
|
|
201
|
+
// R225 hub digit / panel header counts / recent row count
|
|
202
|
+
// R232 chip row counts (working / online / active-links)
|
|
203
|
+
// R321 recent row timestamp
|
|
204
|
+
// R322 recent panel hot count
|
|
205
|
+
// R323 filter pin pill counts
|
|
206
|
+
// R333 vendor count suffix
|
|
207
|
+
// R357 active-links freshness suffix wrapper
|
|
208
|
+
// R377 FreshnessChip body (this round)
|
|
209
|
+
// `font-mono` already gives equal-width glyphs but `tabular-nums`
|
|
210
|
+
// is the explicit-invariant the rest of the chip row carries.
|
|
211
|
+
// FreshnessChip body reads `lag · {sec}s` — the {sec} digit grows
|
|
212
|
+
// every second; tabular-nums explicitly locks digit width so the
|
|
213
|
+
// chip stays planted as the seconds counter ticks past 9 → 10 →
|
|
214
|
+
// 99 → 100. R187 transition-colors duration-300 + R275 stale-only
|
|
215
|
+
// render gate + R315 font-medium R313 family alignment all
|
|
216
|
+
// preserved.
|
|
217
|
+
const baseClass = "hidden sm:inline px-2.5 py-1 rounded-md font-mono font-medium tabular-nums border transition-colors duration-300";
|
|
198
218
|
const colorClass = stale
|
|
199
219
|
? "bg-amber-500/10 text-amber-300 border-amber-500/20"
|
|
200
220
|
: "bg-gray-500/10 text-gray-400 border-gray-500/20";
|
|
@@ -1026,6 +1046,42 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
1026
1046
|
// navigation footer. Drives the on-hover opacity boost + underline
|
|
1027
1047
|
// that signals interactivity, mirroring the hoveredHub idiom above.
|
|
1028
1048
|
const [hoveredRecentMore, setHoveredRecentMore] = useState(false);
|
|
1049
|
+
// Round 346 / Loop: minimap-container hover affordance. The minimap
|
|
1050
|
+
// is a click-target (role=button at line ~7810, recenter-on-click +
|
|
1051
|
+
// Enter→resetView) but pre-R346 nothing visually changed on hover —
|
|
1052
|
+
// the only hint was the `cursor: crosshair` style. R346 lifts the
|
|
1053
|
+
// viewport rect (strokeWidth 1.5 → 1.75 + opacity 0.9 → 1.0) when
|
|
1054
|
+
// the user enters the minimap, marking "this is the recenter target
|
|
1055
|
+
// and it's alive". Sibling polish to the R332 minimap rounded-md →
|
|
1056
|
+
// rounded-lg corner family — that round refined geometry, this one
|
|
1057
|
+
// gives the viewport indicator inside the geometry a hover state.
|
|
1058
|
+
// 280ms ease-out transition list matches R199 smoothView vocabulary
|
|
1059
|
+
// so the visual joins the existing rhythm on the same rect.
|
|
1060
|
+
const [hoveredMinimap, setHoveredMinimap] = useState(false);
|
|
1061
|
+
// Round 347 / Loop: zoom-level readout hover-state letter-spacing
|
|
1062
|
+
// tween (0 → 0.5 px). The readout sandwiched between zoom-out /
|
|
1063
|
+
// zoom-in is a passive percent display — pre-R347 it had no hover
|
|
1064
|
+
// feedback at all (only a `title` tooltip). R347 extends the R344
|
|
1065
|
+
// (`+N more flows` footer) + R345 (panel titles) hover-letter-
|
|
1066
|
+
// spacing family from panel/footer surfaces into the HTML chrome
|
|
1067
|
+
// strip. Hovering the readout spreads its digits 0.5 px, signalling
|
|
1068
|
+
// "this is alive". tabular-nums + minWidth: 46 from R225 still lock
|
|
1069
|
+
// the column so the tween doesn't shove neighbouring controls.
|
|
1070
|
+
// 200ms ease-out joins the existing R264 color/border transition
|
|
1071
|
+
// list on the same span.
|
|
1072
|
+
const [hoveredZoomLevel, setHoveredZoomLevel] = useState(false);
|
|
1073
|
+
// Round 350 / Loop: reset-button icon hover-rotate preview of the
|
|
1074
|
+
// R184 click-spin. Pre-R350 hovering the reset button only changed
|
|
1075
|
+
// the button bg (white/5); the icon inside stayed perfectly still.
|
|
1076
|
+
// R350 nudges the icon -8° on hover — a tactile hint that this
|
|
1077
|
+
// button rotates the icon on click. When the click fires, the
|
|
1078
|
+
// R184 anet-reset-spin keyframe animation overrides the hover
|
|
1079
|
+
// transform for its 450 ms run (CSS animations win over transitions
|
|
1080
|
+
// on the same property); when the animation ends + React removes
|
|
1081
|
+
// the className, the inline transform eases back to whatever the
|
|
1082
|
+
// hover state says — either -8° (still hovering) or 0 (mouse left).
|
|
1083
|
+
// 350th-round milestone polish.
|
|
1084
|
+
const [hoveredReset, setHoveredReset] = useState(false);
|
|
1029
1085
|
// R135: panel-wide hover-elevation. The recent-signal + legend
|
|
1030
1086
|
// panels both already host clickable rows (R56/R116 recent rows,
|
|
1031
1087
|
// R55/R61 legend rows) and a clickable footer (R133), so the
|
|
@@ -1787,12 +1843,30 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
1787
1843
|
while honoring R328's wider baseline rhythm. data-topo-
|
|
1788
1844
|
chrome-layout-trailer attr unchanged — it still marks
|
|
1789
1845
|
the boundary surface for the gap probe. */}
|
|
1846
|
+
{/* Round 375 / Loop: Layout-toggle wrapper rounded-md → rounded-
|
|
1847
|
+
lg (6 → 8 px). Extends the corner-radius cascade family
|
|
1848
|
+
to the chrome-strip layout-toggle wrapper:
|
|
1849
|
+
R330 canvas wrapper rounded-xl 12 px
|
|
1850
|
+
R331 SVG panels rx=10 10 px
|
|
1851
|
+
R332 minimap container rounded-lg 8 px
|
|
1852
|
+
R375 Layout-toggle wrapper rounded-lg 8 px (this round)
|
|
1853
|
+
Pre-R375 the wrapper at rounded-md (6 px) was the only
|
|
1854
|
+
chrome-strip container still using the smaller corner
|
|
1855
|
+
radius — both R330 outer wrapper and R332 minimap sit at
|
|
1856
|
+
≥ 8 px, so the Layout toggle's 6 px stood out as a
|
|
1857
|
+
tighter corner against the family. R375 brings it into
|
|
1858
|
+
the rounded-lg tier where the minimap already lives.
|
|
1859
|
+
Pure paint change — overflow-hidden still clips the
|
|
1860
|
+
inner buttons' bg-cyan-500/15 tints; no layout shift.
|
|
1861
|
+
R268 border-color + 200ms transition + R329 mr-0.5 +
|
|
1862
|
+
data-topo-chrome-layout-trailer all preserved. */}
|
|
1790
1863
|
<div
|
|
1791
|
-
className="mr-0.5 inline-flex rounded-
|
|
1864
|
+
className="mr-0.5 inline-flex rounded-lg border overflow-hidden"
|
|
1792
1865
|
style={{ borderColor: pal.containerBorder, transition: 'border-color 200ms ease-out' }}
|
|
1793
1866
|
role="group"
|
|
1794
1867
|
aria-label="Topology layout"
|
|
1795
1868
|
data-topo-chrome-layout-trailer
|
|
1869
|
+
data-topo-chrome-layout-radius="rounded-lg"
|
|
1796
1870
|
>
|
|
1797
1871
|
<button
|
|
1798
1872
|
onClick={() => { popChrome('layout-ring'); if (layout !== 'ring') toggleLayout(); }}
|
|
@@ -1826,7 +1900,17 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
1826
1900
|
weight; cyan-400/60 + ring-inset retained. The
|
|
1827
1901
|
R163/R196 hover/active deeps + R249 chrome-pop
|
|
1828
1902
|
click feedback continue unchanged. */
|
|
1829
|
-
|
|
1903
|
+
// R351: hover:tracking-wide extends the R344/R345/R347
|
|
1904
|
+
// hover-letter-spacing family to a 4th surface (chrome-
|
|
1905
|
+
// strip Ring/Grid pair). transition-colors className
|
|
1906
|
+
// dropped in favour of an inline transition spec that
|
|
1907
|
+
// bundles bg/color (150ms ease) + letter-spacing
|
|
1908
|
+
// (200ms ease-out) — Tailwind's transition-colors
|
|
1909
|
+
// doesn't list letter-spacing, so without this the
|
|
1910
|
+
// hover:tracking-wide would snap. Sibling change on
|
|
1911
|
+
// the Grid button below.
|
|
1912
|
+
className={`px-2.5 py-1 focus:outline-none focus-visible:ring-1 focus-visible:ring-cyan-400/60 focus-visible:ring-inset hover:tracking-wide ${layout === 'ring' ? 'bg-cyan-500/15 text-cyan-300 font-medium hover:bg-cyan-500/20 active:bg-cyan-500/25' : 'text-gray-400 hover:text-cyan-300 hover:bg-cyan-500/5 active:bg-cyan-500/15'} ${chromePopping === 'layout-ring' ? ' anet-chrome-pop' : ''}`}
|
|
1913
|
+
style={{ transition: 'background-color 150ms ease, color 150ms ease, letter-spacing 200ms ease-out' }}
|
|
1830
1914
|
>
|
|
1831
1915
|
Ring
|
|
1832
1916
|
</button>
|
|
@@ -1843,14 +1927,20 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
1843
1927
|
// Round 306 / Loop: focus-visible:ring-2 → ring-1 sibling
|
|
1844
1928
|
// change to Ring above — unifies focus-ring width across
|
|
1845
1929
|
// all chrome buttons.
|
|
1846
|
-
|
|
1930
|
+
// R351 sibling — Grid button picks up hover:tracking-wide
|
|
1931
|
+
// + inline transition spec. Same vocabulary as Ring.
|
|
1932
|
+
className={`px-2.5 py-1 border-l focus:outline-none focus-visible:ring-1 focus-visible:ring-cyan-400/60 focus-visible:ring-inset hover:tracking-wide ${layout === 'grid' ? 'bg-cyan-500/15 text-cyan-300 font-medium hover:bg-cyan-500/20 active:bg-cyan-500/25' : 'text-gray-400 hover:text-cyan-300 hover:bg-cyan-500/5 active:bg-cyan-500/15'} ${chromePopping === 'layout-grid' ? ' anet-chrome-pop' : ''}`}
|
|
1847
1933
|
/* Round 268 / Loop: Grid button's left border (the
|
|
1848
1934
|
internal divider between Ring and Grid) picks up
|
|
1849
1935
|
pal.containerBorder, matching the wrapper change at
|
|
1850
1936
|
line ~1460 and the chrome strip's segmented borders
|
|
1851
|
-
(nodeSize, zoom). transition-colors className
|
|
1852
|
-
the border-color
|
|
1853
|
-
|
|
1937
|
+
(nodeSize, zoom). The R268 transition-colors className
|
|
1938
|
+
used to carry the border-color ease; R351 unfolds the
|
|
1939
|
+
transition list into the inline spec below so the
|
|
1940
|
+
letter-spacing tween rides alongside without snapping
|
|
1941
|
+
the border-color flip — border-color 200ms ease-out
|
|
1942
|
+
keeps R268's theme-toggle smoothness intact. */
|
|
1943
|
+
style={{ borderColor: pal.containerBorder, transition: 'background-color 150ms ease, color 150ms ease, border-color 200ms ease-out, letter-spacing 200ms ease-out' }}
|
|
1854
1944
|
>
|
|
1855
1945
|
Grid
|
|
1856
1946
|
</button>
|
|
@@ -2011,7 +2101,17 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
2011
2101
|
pattern: small label spans demote, value stays
|
|
2012
2102
|
prominent. data-working-chip-unit exposes the
|
|
2013
2103
|
span for tests. */}
|
|
2014
|
-
{
|
|
2104
|
+
{/* Round 362 / Loop: digit picks up font-semibold
|
|
2105
|
+
(fw 500 → 600) for within-chip weight tier. The
|
|
2106
|
+
chip's outer className stays at font-medium (R313
|
|
2107
|
+
data-weight baseline); the digit overrides to
|
|
2108
|
+
semibold so it reads heavier than its " working"
|
|
2109
|
+
unit (which keeps fw 500 + R338 opacity-70).
|
|
2110
|
+
Joins the R333-R341 chip-internal-hierarchy arc
|
|
2111
|
+
at the chip-count scope. Sibling edits on the
|
|
2112
|
+
online + active-links chip digits below. data-
|
|
2113
|
+
working-chip-digit attr exposes the digit span. */}
|
|
2114
|
+
<span className="font-semibold" data-working-chip-digit>{workingCount}</span><span className="opacity-70" data-working-chip-unit> working</span>
|
|
2015
2115
|
</span>
|
|
2016
2116
|
<span
|
|
2017
2117
|
// Round 201 / Loop: online chip — mirror of the working
|
|
@@ -2074,7 +2174,8 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
2074
2174
|
}}
|
|
2075
2175
|
>
|
|
2076
2176
|
{/* R337 sibling — online chip unit demotion. */}
|
|
2077
|
-
{
|
|
2177
|
+
{/* R362 sibling — online-chip digit gains font-semibold. */}
|
|
2178
|
+
<span className="font-semibold" data-online-chip-digit>{onlineNodes.length}</span><span className="opacity-70" data-online-chip-unit> online</span>
|
|
2078
2179
|
</span>
|
|
2079
2180
|
</>
|
|
2080
2181
|
);
|
|
@@ -2201,8 +2302,45 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
2201
2302
|
title={`${w} working · ${i} idle · ${o} offline`}
|
|
2202
2303
|
data-fleet-pressure
|
|
2203
2304
|
>
|
|
2204
|
-
|
|
2205
|
-
|
|
2305
|
+
{/* Round 373 / Loop: pressure-bar kicker label gains
|
|
2306
|
+
font-medium (fw 400 → 500). Sibling small-text fw
|
|
2307
|
+
lift family with R363 recent-row alias + R364
|
|
2308
|
+
legend-row label + R366 group-label count + R368
|
|
2309
|
+
+N more flows footer — extends to a 5th surface
|
|
2310
|
+
(the chip-row's 'pressure' label). At fontSize
|
|
2311
|
+
10 px tracking-wide against the chip's gray bg,
|
|
2312
|
+
the default fw 400 sat below the deliberate-data
|
|
2313
|
+
band; fw 500 brings it into parity with the
|
|
2314
|
+
chip-row 'working / online / active links' unit
|
|
2315
|
+
spans (chip-level font-medium R313). data-fleet-
|
|
2316
|
+
pressure-kicker attr exposes the kicker for tests. */}
|
|
2317
|
+
<span className="text-[10px] tracking-wide font-medium" data-fleet-pressure-kicker>pressure</span>
|
|
2318
|
+
{/* Round 374 / Loop: pressure-bar height h-1.5 → h-2
|
|
2319
|
+
(6 → 8 px) — sibling visual-weight bump (8th anchor
|
|
2320
|
+
in the family):
|
|
2321
|
+
R287 minimap viewport stroke 1 → 1.5
|
|
2322
|
+
R295 legend swatch base radius 5.5 → 6
|
|
2323
|
+
R359 recent-row pip base radius 1.6 → 1.8
|
|
2324
|
+
R360 hub digit fontSize 11 → 12
|
|
2325
|
+
R361 edge-badge digit fontSize 10 → 11
|
|
2326
|
+
R365 hub-highlight base radius 5 → 5.5
|
|
2327
|
+
R367 edge-badge rest stroke 1 → 1.25
|
|
2328
|
+
R374 pressure-bar height 1.5 → 2 (this round)
|
|
2329
|
+
+33 % bar height gives the working/idle/offline
|
|
2330
|
+
segments more visibility — at h-1.5 the 3-segment
|
|
2331
|
+
proportion bar was readable but slim; at h-2 the
|
|
2332
|
+
segments parse cleanly even when one tier is
|
|
2333
|
+
< 10 % share. Geometry-safe: items-center flex
|
|
2334
|
+
centers the bar inside the chip's py-1 (4 px top +
|
|
2335
|
+
4 px bottom) — bar at 8 px stays comfortably
|
|
2336
|
+
inside the 10-px text-row height. R165 segment
|
|
2337
|
+
width transitions + R210 brightness hover + R83
|
|
2338
|
+
pin-mirror box-shadow on segments all preserved
|
|
2339
|
+
(segments inherit width from parent so the height
|
|
2340
|
+
bump propagates without segment-side edits).
|
|
2341
|
+
data-fleet-pressure-bar-height attr exposes the
|
|
2342
|
+
height token for tests. */}
|
|
2343
|
+
<span className="inline-flex h-2 w-16 rounded-full overflow-hidden" style={{ background: 'rgb(75 85 99 / 0.25)' }} data-fleet-pressure-bar-height="h-2">
|
|
2206
2344
|
{seg(w, isLight ? '#059669' : '#22c55e', 'working', 'working')}
|
|
2207
2345
|
{seg(i, isLight ? '#0d9488' : '#2dd4bf', 'idle', 'idle')}
|
|
2208
2346
|
{seg(o, isLight ? '#94a3b8' : '#6b7280', 'offline', 'offline')}
|
|
@@ -2259,7 +2397,10 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
2259
2397
|
data-active-filter="status"
|
|
2260
2398
|
data-filter-match-count={matchCount}
|
|
2261
2399
|
data-filter-match-aliases={matchAliases.join(',')}
|
|
2262
|
-
|
|
2400
|
+
// R355: `group` lets the inner opacity-70 spans (prefix
|
|
2401
|
+
// `filter:` + count `· N`) brighten to 100 % on pill hover.
|
|
2402
|
+
// Sibling treatment on group + vendor pills below.
|
|
2403
|
+
className="group inline-flex items-center gap-1.5 px-2.5 py-1 rounded-md font-mono font-medium text-xs border anet-fade-in anet-topo-chip-focus"
|
|
2263
2404
|
title={matchCount > 0 ? `${matchPreview}${matchSuffix} — click to clear` : 'Click to clear filter'}
|
|
2264
2405
|
onClick={() => setPinnedStatus(null)}
|
|
2265
2406
|
style={{
|
|
@@ -2273,12 +2414,26 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
2273
2414
|
cursor: 'pointer',
|
|
2274
2415
|
}}
|
|
2275
2416
|
>
|
|
2276
|
-
<span><span className="hidden sm:inline opacity-70" data-filter-prefix>filter: </span>{pinnedStatus}<span className="opacity-70 tabular-nums" data-filter-pill-count> · {matchCount}</span></span>
|
|
2417
|
+
<span><span className="hidden sm:inline opacity-70 transition-opacity duration-200 group-hover:opacity-100" data-filter-prefix>filter: </span>{pinnedStatus}<span className="opacity-70 tabular-nums transition-opacity duration-200 group-hover:opacity-100" data-filter-pill-count> · {matchCount}</span></span>
|
|
2277
2418
|
<button
|
|
2278
2419
|
type="button"
|
|
2279
2420
|
aria-label={`Clear ${pinnedStatus} filter`}
|
|
2280
2421
|
onClick={(e) => { e.stopPropagation(); setPinnedStatus(null); }}
|
|
2281
|
-
|
|
2422
|
+
/* Round 356 / Loop: filter pin pill × buttons gain
|
|
2423
|
+
hover:scale-110 (Tailwind 4 modern CSS `scale` property,
|
|
2424
|
+
not legacy transform). Sibling polish to R354 vendor
|
|
2425
|
+
letter glyph + R350/R352/R353 chrome icon hover-scales.
|
|
2426
|
+
Pre-R356 the × had only hover:opacity-70 — the target
|
|
2427
|
+
dimmed under cursor but didn't lift. R356 adds a 10 %
|
|
2428
|
+
scale on hover so the click-target reads as "press me"
|
|
2429
|
+
alongside the dim. transform-gpu hint promotes the
|
|
2430
|
+
button to its own compositor layer for crisper edges
|
|
2431
|
+
during the scale tween. transition-transform duration-
|
|
2432
|
+
200 matches the chrome icon hover-scale timing family.
|
|
2433
|
+
inline-block is default for <button> so no display
|
|
2434
|
+
tweak needed. replace_all covers all 4 filter pin
|
|
2435
|
+
pills (status / group / vendor / edge) at once. */
|
|
2436
|
+
className="ml-0.5 leading-none hover:opacity-70 transition-transform duration-200 ease-out hover:scale-110 transform-gpu"
|
|
2282
2437
|
style={{ background: 'transparent', color: 'inherit', cursor: 'pointer', padding: 0 }}
|
|
2283
2438
|
>×</button>
|
|
2284
2439
|
</span>
|
|
@@ -2297,7 +2452,8 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
2297
2452
|
data-active-filter="group"
|
|
2298
2453
|
data-filter-match-count={matchCount}
|
|
2299
2454
|
data-filter-match-aliases={matchAliases.join(',')}
|
|
2300
|
-
|
|
2455
|
+
// R355 sibling — `group` parent + group-hover on inner spans.
|
|
2456
|
+
className="group inline-flex items-center gap-1.5 px-2.5 py-1 rounded-md font-mono font-medium text-xs border anet-fade-in anet-topo-chip-focus"
|
|
2301
2457
|
title={matchCount > 0 ? `${matchPreview}${matchSuffix} — click to clear` : 'Click to clear filter'}
|
|
2302
2458
|
onClick={() => setPinnedGroup(null)}
|
|
2303
2459
|
style={{
|
|
@@ -2307,12 +2463,26 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
2307
2463
|
cursor: 'pointer',
|
|
2308
2464
|
}}
|
|
2309
2465
|
>
|
|
2310
|
-
<span><span className="hidden sm:inline opacity-70" data-filter-prefix>filter: </span>{pinnedGroup}<span className="opacity-70 tabular-nums" data-filter-pill-count> · {matchCount}</span></span>
|
|
2466
|
+
<span><span className="hidden sm:inline opacity-70 transition-opacity duration-200 group-hover:opacity-100" data-filter-prefix>filter: </span>{pinnedGroup}<span className="opacity-70 tabular-nums transition-opacity duration-200 group-hover:opacity-100" data-filter-pill-count> · {matchCount}</span></span>
|
|
2311
2467
|
<button
|
|
2312
2468
|
type="button"
|
|
2313
2469
|
aria-label={`Clear group filter ${pinnedGroup}`}
|
|
2314
2470
|
onClick={(e) => { e.stopPropagation(); setPinnedGroup(null); }}
|
|
2315
|
-
|
|
2471
|
+
/* Round 356 / Loop: filter pin pill × buttons gain
|
|
2472
|
+
hover:scale-110 (Tailwind 4 modern CSS `scale` property,
|
|
2473
|
+
not legacy transform). Sibling polish to R354 vendor
|
|
2474
|
+
letter glyph + R350/R352/R353 chrome icon hover-scales.
|
|
2475
|
+
Pre-R356 the × had only hover:opacity-70 — the target
|
|
2476
|
+
dimmed under cursor but didn't lift. R356 adds a 10 %
|
|
2477
|
+
scale on hover so the click-target reads as "press me"
|
|
2478
|
+
alongside the dim. transform-gpu hint promotes the
|
|
2479
|
+
button to its own compositor layer for crisper edges
|
|
2480
|
+
during the scale tween. transition-transform duration-
|
|
2481
|
+
200 matches the chrome icon hover-scale timing family.
|
|
2482
|
+
inline-block is default for <button> so no display
|
|
2483
|
+
tweak needed. replace_all covers all 4 filter pin
|
|
2484
|
+
pills (status / group / vendor / edge) at once. */
|
|
2485
|
+
className="ml-0.5 leading-none hover:opacity-70 transition-transform duration-200 ease-out hover:scale-110 transform-gpu"
|
|
2316
2486
|
style={{ background: 'transparent', color: 'inherit', cursor: 'pointer', padding: 0 }}
|
|
2317
2487
|
>×</button>
|
|
2318
2488
|
</span>
|
|
@@ -2347,7 +2517,8 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
2347
2517
|
data-active-filter="vendor"
|
|
2348
2518
|
data-filter-match-count={matchCount}
|
|
2349
2519
|
data-filter-match-aliases={matchAliases.join(',')}
|
|
2350
|
-
|
|
2520
|
+
// R355 sibling — `group` parent + group-hover on inner spans.
|
|
2521
|
+
className="group inline-flex items-center gap-1.5 px-2.5 py-1 rounded-md font-mono font-medium text-xs border anet-fade-in anet-topo-chip-focus"
|
|
2351
2522
|
title={matchCount > 0 ? `${matchPreview}${matchSuffix} — click to clear` : 'Click to clear vendor filter'}
|
|
2352
2523
|
onClick={() => setPinnedVendor(null)}
|
|
2353
2524
|
style={{
|
|
@@ -2357,12 +2528,26 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
2357
2528
|
cursor: 'pointer',
|
|
2358
2529
|
}}
|
|
2359
2530
|
>
|
|
2360
|
-
<span><span className="hidden sm:inline opacity-70" data-filter-prefix>filter: </span>{pinnedVendor}<span className="opacity-70 tabular-nums" data-filter-pill-count> · {matchCount}</span></span>
|
|
2531
|
+
<span><span className="hidden sm:inline opacity-70 transition-opacity duration-200 group-hover:opacity-100" data-filter-prefix>filter: </span>{pinnedVendor}<span className="opacity-70 tabular-nums transition-opacity duration-200 group-hover:opacity-100" data-filter-pill-count> · {matchCount}</span></span>
|
|
2361
2532
|
<button
|
|
2362
2533
|
type="button"
|
|
2363
2534
|
aria-label={`Clear vendor filter ${pinnedVendor}`}
|
|
2364
2535
|
onClick={(e) => { e.stopPropagation(); setPinnedVendor(null); }}
|
|
2365
|
-
|
|
2536
|
+
/* Round 356 / Loop: filter pin pill × buttons gain
|
|
2537
|
+
hover:scale-110 (Tailwind 4 modern CSS `scale` property,
|
|
2538
|
+
not legacy transform). Sibling polish to R354 vendor
|
|
2539
|
+
letter glyph + R350/R352/R353 chrome icon hover-scales.
|
|
2540
|
+
Pre-R356 the × had only hover:opacity-70 — the target
|
|
2541
|
+
dimmed under cursor but didn't lift. R356 adds a 10 %
|
|
2542
|
+
scale on hover so the click-target reads as "press me"
|
|
2543
|
+
alongside the dim. transform-gpu hint promotes the
|
|
2544
|
+
button to its own compositor layer for crisper edges
|
|
2545
|
+
during the scale tween. transition-transform duration-
|
|
2546
|
+
200 matches the chrome icon hover-scale timing family.
|
|
2547
|
+
inline-block is default for <button> so no display
|
|
2548
|
+
tweak needed. replace_all covers all 4 filter pin
|
|
2549
|
+
pills (status / group / vendor / edge) at once. */
|
|
2550
|
+
className="ml-0.5 leading-none hover:opacity-70 transition-transform duration-200 ease-out hover:scale-110 transform-gpu"
|
|
2366
2551
|
style={{ background: 'transparent', color: 'inherit', cursor: 'pointer', padding: 0 }}
|
|
2367
2552
|
>×</button>
|
|
2368
2553
|
</span>
|
|
@@ -2439,7 +2624,21 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
2439
2624
|
type="button"
|
|
2440
2625
|
aria-label={`Clear edge filter ${link.from} → ${link.to}`}
|
|
2441
2626
|
onClick={(e) => { e.stopPropagation(); setPinnedEdgeKey(null); }}
|
|
2442
|
-
|
|
2627
|
+
/* Round 356 / Loop: filter pin pill × buttons gain
|
|
2628
|
+
hover:scale-110 (Tailwind 4 modern CSS `scale` property,
|
|
2629
|
+
not legacy transform). Sibling polish to R354 vendor
|
|
2630
|
+
letter glyph + R350/R352/R353 chrome icon hover-scales.
|
|
2631
|
+
Pre-R356 the × had only hover:opacity-70 — the target
|
|
2632
|
+
dimmed under cursor but didn't lift. R356 adds a 10 %
|
|
2633
|
+
scale on hover so the click-target reads as "press me"
|
|
2634
|
+
alongside the dim. transform-gpu hint promotes the
|
|
2635
|
+
button to its own compositor layer for crisper edges
|
|
2636
|
+
during the scale tween. transition-transform duration-
|
|
2637
|
+
200 matches the chrome icon hover-scale timing family.
|
|
2638
|
+
inline-block is default for <button> so no display
|
|
2639
|
+
tweak needed. replace_all covers all 4 filter pin
|
|
2640
|
+
pills (status / group / vendor / edge) at once. */
|
|
2641
|
+
className="ml-0.5 leading-none hover:opacity-70 transition-transform duration-200 ease-out hover:scale-110 transform-gpu"
|
|
2443
2642
|
style={{ background: 'transparent', color: 'inherit', cursor: 'pointer', padding: 0 }}
|
|
2444
2643
|
>×</button>
|
|
2445
2644
|
</span>
|
|
@@ -2750,7 +2949,64 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
2750
2949
|
}
|
|
2751
2950
|
}}
|
|
2752
2951
|
>
|
|
2753
|
-
|
|
2952
|
+
{/* Round 354 / Loop: vendor letter glyph scales
|
|
2953
|
+
1.0 → 1.1 on hover. R88 already dims OTHER
|
|
2954
|
+
vendors on hover via canvas-wide opacity
|
|
2955
|
+
masking; R202 added a chip-level bg tint
|
|
2956
|
+
(color-mix 12 % alpha) so the chip itself
|
|
2957
|
+
responds. R354 closes the trio with a glyph-
|
|
2958
|
+
level lift: the focused vendor LETTER actively
|
|
2959
|
+
rises (transform scale) rather than the chip
|
|
2960
|
+
merely changing colour. Three layers of positive
|
|
2961
|
+
feedback on the hovered vendor + canvas-wide
|
|
2962
|
+
negative feedback on the others — a clean
|
|
2963
|
+
figure/ground separation.
|
|
2964
|
+
|
|
2965
|
+
display: inline-block is required for transform
|
|
2966
|
+
to apply (inline elements ignore transform).
|
|
2967
|
+
transformOrigin: 'center' so the glyph pivots
|
|
2968
|
+
around its centre instead of arcing from the
|
|
2969
|
+
baseline anchor. transition rides the existing
|
|
2970
|
+
Tailwind 4 transform/scale list (no new
|
|
2971
|
+
property — Tailwind already lists transform in
|
|
2972
|
+
the default transition-property set). 200ms
|
|
2973
|
+
matches the R202 chip bg-tint timing so the
|
|
2974
|
+
glyph lift and chip background ease in concert. */}
|
|
2975
|
+
{/* Round 369 / Loop: vendor letter glyph picks up
|
|
2976
|
+
fontWeight 600 (font-semibold). The glyph is the
|
|
2977
|
+
vendor identifier — the DATA the operator scans
|
|
2978
|
+
in this chip (A / O / 书 / C / G / ?). R333 set
|
|
2979
|
+
the count suffix `:N` to text-gray-400 + tabular-
|
|
2980
|
+
nums and (via parent inheritance) fw 500. Pre-
|
|
2981
|
+
R369 the LETTER also inherited fw 500 from the
|
|
2982
|
+
chip's font-medium — letter and count read at
|
|
2983
|
+
the same weight, contradicting the data-vs-label
|
|
2984
|
+
hierarchy the rest of the chip-row already speaks.
|
|
2985
|
+
R369 lifts the letter to fw 600 so the chip now
|
|
2986
|
+
reads as the same two-tier pattern R362 closed
|
|
2987
|
+
on the working / online / active-links chips:
|
|
2988
|
+
chip digit/letter fw 600 (data)
|
|
2989
|
+
chip unit/count fw 500 (label)
|
|
2990
|
+
Sibling treatment to R362 — extends the R333-R341
|
|
2991
|
+
chip-internal-hierarchy arc to the vendor-letter
|
|
2992
|
+
chip surface (9th surface family). R354 transform-
|
|
2993
|
+
scale-on-hover + R88 canvas-dim-others + R202
|
|
2994
|
+
chip bg color-mix all preserved on the same span.
|
|
2995
|
+
data-vendor-letter-glyph-font-weight attr exposes
|
|
2996
|
+
the value for tests. */}
|
|
2997
|
+
<span
|
|
2998
|
+
data-vendor-letter-glyph={v.initial}
|
|
2999
|
+
data-vendor-letter-glyph-hover={hoveredVendor === v.initial ? 'true' : 'false'}
|
|
3000
|
+
data-vendor-letter-glyph-font-weight="600"
|
|
3001
|
+
style={{
|
|
3002
|
+
color: v.color,
|
|
3003
|
+
display: 'inline-block',
|
|
3004
|
+
fontWeight: 600,
|
|
3005
|
+
transform: hoveredVendor === v.initial ? 'scale(1.1)' : 'scale(1)',
|
|
3006
|
+
transformOrigin: 'center',
|
|
3007
|
+
transition: 'transform 200ms ease-out',
|
|
3008
|
+
}}
|
|
3009
|
+
>{v.initial}</span>
|
|
2754
3010
|
{/* Round 333 / Loop: vendor count suffix `:{N}` joins
|
|
2755
3011
|
the R317 subordinate-text-lift family (gray-500 →
|
|
2756
3012
|
gray-400) plus picks up tabular-nums for digit
|
|
@@ -2884,7 +3140,8 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
2884
3140
|
the 5th chip surface in the R333/R335/R336/R337
|
|
2885
3141
|
chip-internal-hierarchy arc. data-active-links-
|
|
2886
3142
|
chip-unit exposes the unit span for tests. */}
|
|
2887
|
-
{
|
|
3143
|
+
{/* R362 sibling — active-links chip digit gains font-semibold. */}
|
|
3144
|
+
<span className="font-semibold" data-active-links-chip-digit>{flowLinks.length}</span><span className="opacity-70" data-active-links-chip-unit> active link{flowLinks.length === 1 ? '' : 's'}</span>
|
|
2888
3145
|
{rel ? (() => {
|
|
2889
3146
|
// Round 161 / Loop: extend R160's recency-pip
|
|
2890
3147
|
// vocabulary up one scope — from per-flow row to
|
|
@@ -2909,8 +3166,8 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
2909
3166
|
const alpha = ageSec <= 30
|
|
2910
3167
|
? 1
|
|
2911
3168
|
: ageSec <= 300
|
|
2912
|
-
? 1 - ((ageSec - 30) / 270) * 0.
|
|
2913
|
-
: 0.25
|
|
3169
|
+
? 1 - ((ageSec - 30) / 270) * 0.70 /* R358: floor 0.25 → 0.30 lift across 3 freshness scopes */
|
|
3170
|
+
: 0.30; /* R358: stale floor lifted 0.25 → 0.30 — 20% legibility bump while preserving fresh/stale ratio */
|
|
2914
3171
|
// Cyan dark / teal light to match palette legendAccent.
|
|
2915
3172
|
const dotColor = isLight
|
|
2916
3173
|
? `rgba(13, 148, 136, ${alpha.toFixed(2)})`
|
|
@@ -2927,7 +3184,28 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
2927
3184
|
// color: dotColor — the lift only affects the trailing
|
|
2928
3185
|
// literal "last {rel}" text.
|
|
2929
3186
|
return (
|
|
2930
|
-
|
|
3187
|
+
// Round 357 / Loop: active-links chip freshness
|
|
3188
|
+
// suffix wrapper picks up `tabular-nums` for digit
|
|
3189
|
+
// width-lock. Pre-R357 the literal "last {rel}"
|
|
3190
|
+
// text (e.g. "last 5s ago", "last 10s ago",
|
|
3191
|
+
// "last 1m ago") had natural-figure digits — the
|
|
3192
|
+
// freshness ticker updates every second, so the
|
|
3193
|
+
// 9→10 boundary on the seconds counter and the
|
|
3194
|
+
// 59→60s → 1m flip both jittered ~1-2 px of glyph
|
|
3195
|
+
// width which propagated through the chip-row's
|
|
3196
|
+
// inline-flex layout, nudging the freshness DOT
|
|
3197
|
+
// and the chip's left edge. Tabular-nums on the
|
|
3198
|
+
// wrapper applies to all descendant digits only
|
|
3199
|
+
// (letters render at natural widths) so the
|
|
3200
|
+
// ticker stays planted across every count cross.
|
|
3201
|
+
// Joins the R224-R232 info-density tabular-nums
|
|
3202
|
+
// sweep at the chip-row freshness scope. Pure
|
|
3203
|
+
// paint-level change, no geometry shift on rest.
|
|
3204
|
+
// The R342 text-gray-400 lift + R161 dot freshness
|
|
3205
|
+
// alpha ramp + R317 subordinate-text-lift family
|
|
3206
|
+
// all preserved. data-active-links-freshness-
|
|
3207
|
+
// wrapper attr exposes the wrapper for tests.
|
|
3208
|
+
<span className="text-gray-400 tabular-nums" data-active-links-freshness-wrapper>
|
|
2931
3209
|
<span
|
|
2932
3210
|
data-active-links-freshness-dot
|
|
2933
3211
|
data-active-links-freshness-alpha={alpha.toFixed(2)}
|
|
@@ -3591,6 +3869,20 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
3591
3869
|
active surfaces the activity state for test probes
|
|
3592
3870
|
(active spokes don't carry the bucket/dur attrs so
|
|
3593
3871
|
they need their own data anchor). */
|
|
3872
|
+
// Round 382 / Loop: hub-spoke path picks up
|
|
3873
|
+
// strokeLinecap='round'. Sibling polish to R378 flow-
|
|
3874
|
+
// rail dashes + R380 group box dashes — three dashed-
|
|
3875
|
+
// stroke surfaces now share 'round' linecap:
|
|
3876
|
+
// R378 flow-rail '2 12' -> soft 3-px pills
|
|
3877
|
+
// R380 group box '6 6' -> soft 7.5-px pills
|
|
3878
|
+
// R382 hub spoke '6 14' -> soft 7-px pills (this round)
|
|
3879
|
+
// For idle spokes (dashed at sw=1), each 6-px dash gains
|
|
3880
|
+
// 0.5-px round caps and reads as a soft pill instead of
|
|
3881
|
+
// a sharp 6 x 1 rectangle. Active spokes (solid, no
|
|
3882
|
+
// dasharray) have caps mostly hidden by the hub center +
|
|
3883
|
+
// node radius. Geometry-safe; paint-only. R51 sentinel
|
|
3884
|
+
// strokeWidth 1.5/3 untouched (idle=1, active=2). data-
|
|
3885
|
+
// topo-hub-spoke-linecap attr exposes the value for tests.
|
|
3594
3886
|
return (
|
|
3595
3887
|
<path
|
|
3596
3888
|
key={`hub-${session.alias}`}
|
|
@@ -3599,11 +3891,13 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
3599
3891
|
stroke={isActiveSpoke ? pal.spokeStroke.active : pal.spokeStroke.idle}
|
|
3600
3892
|
strokeWidth={isActiveSpoke ? 2 : 1}
|
|
3601
3893
|
strokeDasharray={isActiveSpoke ? 'none' : '6 14'}
|
|
3894
|
+
strokeLinecap="round"
|
|
3602
3895
|
opacity={isActiveSpoke ? 0.7 : 0.45}
|
|
3603
3896
|
className={isActiveSpoke ? undefined : 'anet-topo-spoke-flow'}
|
|
3604
3897
|
data-topo-spoke-bucket={isActiveSpoke ? undefined : busy}
|
|
3605
3898
|
data-topo-spoke-dur={isActiveSpoke ? undefined : spokeDur}
|
|
3606
3899
|
data-topo-hub-spoke-active={isActiveSpoke ? 'true' : 'false'}
|
|
3900
|
+
data-topo-hub-spoke-linecap="round"
|
|
3607
3901
|
style={{
|
|
3608
3902
|
transition: 'stroke 250ms ease-out, stroke-width 250ms ease-out, opacity 250ms ease-out',
|
|
3609
3903
|
...(isActiveSpoke ? {} : {
|
|
@@ -3695,7 +3989,34 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
3695
3989
|
stroke={(isPinned || isHovered) ? pal.legendAccent : pal.ringStroke}
|
|
3696
3990
|
strokeWidth={isPinned ? 3 : isHovered ? 2 : 1.5}
|
|
3697
3991
|
strokeDasharray={(isPinned || isHovered) ? 'none' : '6 6'}
|
|
3992
|
+
/* Round 380 / Loop: cluster box stroke gets round
|
|
3993
|
+
linecap + round linejoin. Sibling SVG stroke-
|
|
3994
|
+
softening polish to R378 flow-rail linecap + R379
|
|
3995
|
+
minimap viewport linejoin — extends the family to
|
|
3996
|
+
the group cluster boundary box (grid layout only):
|
|
3997
|
+
R288 chrome icons strokeLinecap='round'
|
|
3998
|
+
R378 flow-rail dashes strokeLinecap='round'
|
|
3999
|
+
R380 group box dashes strokeLinecap='round' (this round)
|
|
4000
|
+
R379 viewport rect strokeLinejoin='round'
|
|
4001
|
+
R380 group box corners strokeLinejoin='round' (this round)
|
|
4002
|
+
Linecap rounds the R85 '6 6' marching-ants dash
|
|
4003
|
+
pills at rest — each 6 px dash gains a ~0.75 px
|
|
4004
|
+
round cap (sw=1.5 idle), reading as soft pills
|
|
4005
|
+
instead of sharp 6 × 1.5 px rectangles. Linejoin
|
|
4006
|
+
rounds the 4 sharp 90° corners (any state — solid
|
|
4007
|
+
or dashed); at sw=1.5 the join arc is ~0.75 px,
|
|
4008
|
+
matching R379 viewport vocabulary. Geometry-safe:
|
|
4009
|
+
stroke-* properties only affect paint, not bbox.
|
|
4010
|
+
The R51 sentinel 1.5/3 strokeWidth values stay
|
|
4011
|
+
intact (the overlap probe is gated to g[data-
|
|
4012
|
+
node], so this cluster-internal rect is invisible
|
|
4013
|
+
to it anyway). data-group-box-linecap + -linejoin
|
|
4014
|
+
attrs expose the values for tests. */
|
|
4015
|
+
strokeLinecap="round"
|
|
4016
|
+
strokeLinejoin="round"
|
|
3698
4017
|
data-group-box-pinned={isPinned ? 'true' : 'false'}
|
|
4018
|
+
data-group-box-linecap="round"
|
|
4019
|
+
data-group-box-linejoin="round"
|
|
3699
4020
|
// R85: ambient "marching ants" drift on the perimeter
|
|
3700
4021
|
// when this group has at least one working member, and
|
|
3701
4022
|
// neither pin nor hover is active (those treatments
|
|
@@ -3895,12 +4216,33 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
3895
4216
|
lives at a fixed dx=6 offset from the name, so a
|
|
3896
4217
|
digit-width jitter at 9→10 used to shift the
|
|
3897
4218
|
whole count visibly. Tabular locks it. */}
|
|
4219
|
+
{/* Round 366 / Loop: group label member-count tspan
|
|
4220
|
+
fontWeight 400 → 500. Sibling polish to R363
|
|
4221
|
+
recent-row alias text fw 400 → 500 + R364 legend-
|
|
4222
|
+
row label fw 400 → 500 — closes the per-row 'count
|
|
4223
|
+
is fw 500 against label-tier fw 700' pattern at
|
|
4224
|
+
the group-label scope (grid layout cluster mark).
|
|
4225
|
+
Hierarchy snapshot post-R366 across all 3 row
|
|
4226
|
+
surfaces:
|
|
4227
|
+
recent count(hot/cold) fw 700/600 (R320)
|
|
4228
|
+
recent alias fw 500 (R363)
|
|
4229
|
+
legend count fw 600 (R309)
|
|
4230
|
+
legend label fw 500 (R364)
|
|
4231
|
+
group name fw 700 (legacy)
|
|
4232
|
+
group count fw 500 (R366, this round)
|
|
4233
|
+
Monospace family + R225 tabular-nums lock digit
|
|
4234
|
+
width, so the fw bump is paint-only — bbox
|
|
4235
|
+
unchanged + overlap-test invariants hold. R229
|
|
4236
|
+
fill-inherit from parent label (hover-deepen-own-
|
|
4237
|
+
hue family) preserved. data-group-label-count-
|
|
4238
|
+
font-weight attr exposes the value for tests. */}
|
|
3898
4239
|
<tspan
|
|
3899
4240
|
dx="6"
|
|
3900
4241
|
fontSize="11"
|
|
3901
|
-
fontWeight="
|
|
4242
|
+
fontWeight="500"
|
|
3902
4243
|
data-group-label-count={box.key}
|
|
3903
4244
|
data-group-label-count-value={box.count}
|
|
4245
|
+
data-group-label-count-font-weight="500"
|
|
3904
4246
|
style={{ fontVariantNumeric: 'tabular-nums' }}
|
|
3905
4247
|
>· {box.count}</tspan>
|
|
3906
4248
|
{/* Round 58 / Loop: status mix pip strip. Compact text-
|
|
@@ -4189,20 +4531,56 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
4189
4531
|
online chips to splice in additional properties
|
|
4190
4532
|
beside Tailwind's). data-edge-flow-rail attr
|
|
4191
4533
|
surfaces the path for test introspection. */}
|
|
4534
|
+
{/* Round 381 / Loop: edge visible flow path picks up
|
|
4535
|
+
strokeLinecap='round'. Sibling polish to R378
|
|
4536
|
+
flow-rail dashed linecap — both flow-element paths
|
|
4537
|
+
(visible primary + dashed secondary rail) now share
|
|
4538
|
+
'round' linecap vocabulary. The visible path runs
|
|
4539
|
+
source-node → dest-node as one continuous line, so
|
|
4540
|
+
the dest-end is covered by the markerEnd arrow and
|
|
4541
|
+
the source-end usually sits inside the source-node
|
|
4542
|
+
circle. At certain alignments (post-zoom, post-
|
|
4543
|
+
layout-switch transitions), the source-end may peek
|
|
4544
|
+
out by a fraction of a px past the node edge —
|
|
4545
|
+
round caps render that overshoot as a smooth half-
|
|
4546
|
+
disc instead of a sharp rectangle. Pure paint
|
|
4547
|
+
refinement, geometry-safe (bbox of the stroke
|
|
4548
|
+
unchanged at the join with the arrow marker).
|
|
4549
|
+
data-edge-visible-linecap attr exposes the value
|
|
4550
|
+
for tests. */}
|
|
4192
4551
|
<path
|
|
4193
4552
|
d={path}
|
|
4194
4553
|
fill="none"
|
|
4195
4554
|
stroke={pal.flowEdge}
|
|
4196
4555
|
strokeWidth={renderWidth}
|
|
4556
|
+
strokeLinecap="round"
|
|
4197
4557
|
opacity={Math.min(1, (isLight ? 0.22 : 0.28) * fresh * edgeOpacityMul)}
|
|
4198
4558
|
filter={isLight ? undefined : 'url(#topo-glow)'}
|
|
4199
4559
|
markerEnd={`url(#${arrowId})`}
|
|
4200
4560
|
data-edge-visible={link.key}
|
|
4561
|
+
data-edge-visible-linecap="round"
|
|
4201
4562
|
style={{
|
|
4202
4563
|
pointerEvents: 'none',
|
|
4203
4564
|
transition: 'opacity 300ms ease-out, stroke-width 300ms ease-out, stroke 300ms ease-out',
|
|
4204
4565
|
}}
|
|
4205
4566
|
/>
|
|
4567
|
+
{/* Round 378 / Loop: edge flow-path dashed-rail picks
|
|
4568
|
+
up strokeLinecap='round'. Pre-R378 the rail
|
|
4569
|
+
rendered '2 12' dashes as sharp 1×2 rectangles
|
|
4570
|
+
against the canvas backdrop; default 'butt' caps
|
|
4571
|
+
leave dash ends square. R378 rounds each cap so
|
|
4572
|
+
the dashes read as soft 3-px pills (1 px stroke +
|
|
4573
|
+
0.5 px round cap each end). The flow-rail is the
|
|
4574
|
+
secondary 'invisible-spine' line that gives the
|
|
4575
|
+
R57 spoke flow a directional rail to slide along
|
|
4576
|
+
— rounding the dashes softens its presence
|
|
4577
|
+
against the primary visible flow path (R245 has
|
|
4578
|
+
no strokeLinecap so it inherits 'butt' on a
|
|
4579
|
+
continuous line, irrelevant). Geometry-safe:
|
|
4580
|
+
round caps only widen the visible dash; the
|
|
4581
|
+
bbox of the path is unchanged so overlap-test
|
|
4582
|
+
invariants hold. data-edge-flow-rail-linecap
|
|
4583
|
+
attr exposes the value for tests. */}
|
|
4206
4584
|
<path
|
|
4207
4585
|
id={`flow-path-${index}`}
|
|
4208
4586
|
d={path}
|
|
@@ -4210,8 +4588,10 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
4210
4588
|
stroke={pal.flowPath}
|
|
4211
4589
|
strokeWidth="1"
|
|
4212
4590
|
strokeDasharray="2 12"
|
|
4591
|
+
strokeLinecap="round"
|
|
4213
4592
|
opacity={Math.min(1, (isLight ? 0.4 : 0.75) * fresh * edgeOpacityMul)}
|
|
4214
4593
|
data-edge-flow-rail={link.key}
|
|
4594
|
+
data-edge-flow-rail-linecap="round"
|
|
4215
4595
|
style={{ transition: 'opacity 300ms ease-out, stroke 300ms ease-out' }}
|
|
4216
4596
|
/>
|
|
4217
4597
|
{!reducedMotion && (
|
|
@@ -4576,14 +4956,55 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
4576
4956
|
R251 closes the per-edge surface theme-toggle
|
|
4577
4957
|
smoothness — every theme-driven property on
|
|
4578
4958
|
every edge element now eases under cyber↔light. */}
|
|
4959
|
+
{/* Round 367 / Loop: edge midpoint badge rest
|
|
4960
|
+
stroke-width 1 → 1.25. Sibling visual-weight
|
|
4961
|
+
bump family (7th canvas anchor now):
|
|
4962
|
+
R287 minimap viewport stroke 1 → 1.5
|
|
4963
|
+
R295 legend swatch base radius 5.5 → 6
|
|
4964
|
+
R359 recent-row pip base radius 1.6 → 1.8
|
|
4965
|
+
R360 hub digit fontSize 11 → 12
|
|
4966
|
+
R361 edge-badge digit fontSize 10 → 11
|
|
4967
|
+
R365 hub-highlight base radius 5 → 5.5
|
|
4968
|
+
R367 edge-badge rest stroke 1 → 1.25 (this round)
|
|
4969
|
+
Cold edge badges gain ~25 % stroke presence
|
|
4970
|
+
(1.25/1.0 = 1.25). Stays clear of the R51
|
|
4971
|
+
overlap-test sentinel values (1.5 / 3 reserved
|
|
4972
|
+
for node strokes — the test selector is gated
|
|
4973
|
+
to g[data-node] ancestors so this edge-internal
|
|
4974
|
+
circle is invisible to that probe anyway, but
|
|
4975
|
+
1.25 is a safe non-sentinel value regardless).
|
|
4976
|
+
R188 transition list 'stroke-width 300ms ease-
|
|
4977
|
+
out' still smoothes the hot/pin flip — now
|
|
4978
|
+
1.25 → 2 instead of 1 → 2, same ease pace.
|
|
4979
|
+
data-edge-badge-stroke-width-rest exposes the
|
|
4980
|
+
new baseline for tests. */}
|
|
4981
|
+
{/* Round 371 / Loop: edge-badge cyber opacity 0.82
|
|
4982
|
+
→ 0.85. Sibling theme-consistency polish to R370
|
|
4983
|
+
hub hover-ring 0.7 → 0.8. R251 designed this
|
|
4984
|
+
badge with opacity 0.82 (cyber) / 0.95 (light)
|
|
4985
|
+
— 13 % delta. Cyber-theme dark bg needs more
|
|
4986
|
+
alpha to read as 'present'; R371 narrows the
|
|
4987
|
+
gap to 10 %, bringing the badge closer to light
|
|
4988
|
+
theme's 0.95 floor. Light stays at 0.95
|
|
4989
|
+
(already in the legibility band). data-edge-
|
|
4990
|
+
badge-opacity attr exposes the resolved value.
|
|
4991
|
+
Theme-consistency polish family:
|
|
4992
|
+
R246/R247 panel transition family
|
|
4993
|
+
R251 edge badge fill/opacity baseline
|
|
4994
|
+
R370 hub hover-ring cyber 0.7 → 0.8
|
|
4995
|
+
R371 edge badge cyber 0.82 → 0.85 (this round)
|
|
4996
|
+
R164 r=9/10.5 hover-lift + R188/R251 transition
|
|
4997
|
+
list + R367 strokeWidth=1.25 cold rest preserved. */}
|
|
4579
4998
|
<circle
|
|
4580
4999
|
cx={badgeX} cy={badgeY}
|
|
4581
5000
|
r={isHoveredEdge || isPinned ? 10.5 : 9}
|
|
4582
5001
|
fill={pal.legendBox.fill}
|
|
4583
5002
|
stroke={isPinned ? pal.legendHeadline : isHot ? hotStroke : pal.flowEdge}
|
|
4584
|
-
strokeWidth={isPinned ? 2 : isHot ? 2 : 1}
|
|
4585
|
-
opacity={isLight ? 0.95 : 0.
|
|
5003
|
+
strokeWidth={isPinned ? 2 : isHot ? 2 : 1.25}
|
|
5004
|
+
opacity={isLight ? 0.95 : 0.85}
|
|
4586
5005
|
data-edge-badge-lifted={(isHoveredEdge || isPinned) ? 'true' : 'false'}
|
|
5006
|
+
data-edge-badge-stroke-width-rest="1.25"
|
|
5007
|
+
data-edge-badge-opacity={isLight ? 0.95 : 0.85}
|
|
4587
5008
|
style={{ transition: 'r 180ms ease-out, stroke 300ms ease-out, stroke-width 300ms ease-out, fill 200ms ease-out, opacity 200ms ease-out' }}
|
|
4588
5009
|
/>
|
|
4589
5010
|
{/* Round 224 / Loop: edge badge text gains the 4th
|
|
@@ -4622,11 +5043,39 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
4622
5043
|
x={badgeX} y={badgeY + 3}
|
|
4623
5044
|
textAnchor="middle"
|
|
4624
5045
|
fill={pal.legendHeadline}
|
|
4625
|
-
|
|
5046
|
+
/* Round 361 / Loop: edge midpoint badge text
|
|
5047
|
+
fontSize 10 → 11. Sibling visual-weight bump
|
|
5048
|
+
to R360 hub digit 11 → 12. The badge digit
|
|
5049
|
+
is the per-edge equivalent of the hub digit
|
|
5050
|
+
— a high-information scalar (link.count) at
|
|
5051
|
+
a stable canvas position. Pre-R361 fontSize=
|
|
5052
|
+
10 + R220 letter-spacing 0.4 + R224 tabular-
|
|
5053
|
+
nums made the digit READABLE but small
|
|
5054
|
+
against the r=9 / 18-px badge envelope;
|
|
5055
|
+
fontSize=11 nudges the glyph ~10 % bigger
|
|
5056
|
+
(bbox ~7×10 px from ~6×9 px) so the count
|
|
5057
|
+
reads more cleanly at glance — still well
|
|
5058
|
+
inside the r=9 idle circle and the r=10.5
|
|
5059
|
+
hover/pin lift (R164). y=badgeY+3 empirical
|
|
5060
|
+
vertical centring kept (1px drift at the
|
|
5061
|
+
bumped size is below the noise floor in
|
|
5062
|
+
the on-curve flow path).
|
|
5063
|
+
Visual-weight bump family:
|
|
5064
|
+
R287 minimap viewport stroke 1 → 1.5
|
|
5065
|
+
R295 legend swatch base radius 5.5 → 6
|
|
5066
|
+
R359 recent-row pip base radius 1.6 → 1.8
|
|
5067
|
+
R360 hub digit fontSize 11 → 12
|
|
5068
|
+
R361 edge-badge digit fontSize 10 → 11 (this round)
|
|
5069
|
+
data-edge-badge-text-font-size attr exposes
|
|
5070
|
+
the value for tests. R220 pin/hot letter-
|
|
5071
|
+
spacing tween + R224 tabular-nums + R188
|
|
5072
|
+
stroke-width pin/hot transitions all preserved. */
|
|
5073
|
+
fontSize="11"
|
|
4626
5074
|
fontFamily="monospace"
|
|
4627
5075
|
fontWeight="700"
|
|
4628
5076
|
data-edge-badge-text={link.key}
|
|
4629
5077
|
data-edge-badge-text-pin={(isPinned || isHot) ? 'true' : 'false'}
|
|
5078
|
+
data-edge-badge-text-font-size="11"
|
|
4630
5079
|
style={{
|
|
4631
5080
|
pointerEvents: 'none',
|
|
4632
5081
|
fontVariantNumeric: 'tabular-nums',
|
|
@@ -4853,11 +5302,27 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
4853
5302
|
textAnchor="middle"
|
|
4854
5303
|
dy="0.36em"
|
|
4855
5304
|
fill={isLight ? '#d1fae5' : '#ecfdf5'}
|
|
4856
|
-
fontSize
|
|
5305
|
+
/* Round 360 / Loop: hub working-count digit fontSize 11
|
|
5306
|
+
→ 12. The hub is the canvas's focal point — its digit
|
|
5307
|
+
is the most-read scalar on the whole topology. R130
|
|
5308
|
+
sized it at 11 (well inside the r=10 / 20-px core);
|
|
5309
|
+
R360 nudges it to 12 (~13 px wide × 12 px tall, still
|
|
5310
|
+
well inside the 20-px diameter) for ~9 % more presence.
|
|
5311
|
+
Sibling visual-weight bump family:
|
|
5312
|
+
R287 minimap viewport stroke 1 → 1.5
|
|
5313
|
+
R295 legend swatch base radius 5.5 → 6
|
|
5314
|
+
R359 recent-row pip radius 1.6 → 1.8
|
|
5315
|
+
R360 hub digit fontSize 11 → 12 (this round)
|
|
5316
|
+
The R209 scale-1.08-on-hub-hover, R225 tabular-nums,
|
|
5317
|
+
R253 fill transition, R213 always-mount opacity gate
|
|
5318
|
+
all preserved. data-topo-hub-working-count-font-size
|
|
5319
|
+
attr exposes the value for tests. */
|
|
5320
|
+
fontSize="12"
|
|
4857
5321
|
fontFamily="monospace"
|
|
4858
5322
|
fontWeight="700"
|
|
4859
5323
|
opacity={workingCount > 0 ? 1 : 0}
|
|
4860
5324
|
data-topo-hub-working-count={workingCount}
|
|
5325
|
+
data-topo-hub-working-count-font-size="12"
|
|
4861
5326
|
data-topo-hub-working-count-hovered={hoveredHub ? 'true' : 'false'}
|
|
4862
5327
|
data-topo-hub-working-count-visible={workingCount > 0 ? 'true' : 'false'}
|
|
4863
5328
|
// Round 209 / Loop: hub workingCount digit scales 1.0 →
|
|
@@ -4898,12 +5363,53 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
4898
5363
|
{workingCount}
|
|
4899
5364
|
</text>
|
|
4900
5365
|
{/* decorative highlight (visible when workingCount === 0) */}
|
|
5366
|
+
{/* Round 365 / Loop: hub-center 'lit-lamp' decorative highlight
|
|
5367
|
+
circle r 5 → 5.5. Sibling visual-weight bump family —
|
|
5368
|
+
each round lifts one canvas anchor's geometric presence
|
|
5369
|
+
without disturbing its bbox envelope:
|
|
5370
|
+
R287 minimap viewport stroke 1 → 1.5
|
|
5371
|
+
R295 legend swatch base radius 5.5 → 6
|
|
5372
|
+
R359 recent-row pip base radius 1.6 → 1.8
|
|
5373
|
+
R360 hub digit fontSize 11 → 12
|
|
5374
|
+
R361 edge-badge digit fontSize 10 → 11
|
|
5375
|
+
R365 hub-highlight base radius 5 → 5.5 (this round)
|
|
5376
|
+
The highlight only renders when workingCount === 0
|
|
5377
|
+
(decorative 'lamp lit but idle' state per R130 + R213
|
|
5378
|
+
always-mount opacity-gate). At idle, the 0.5-px radius
|
|
5379
|
+
bump (21 % area, π*5.5² / π*5² = 1.21) lifts the lamp's
|
|
5380
|
+
presence — still well inside the r=10 hub-core (R130).
|
|
5381
|
+
opacity=0 when working preserved so the hub-digit's R130
|
|
5382
|
+
takeover stays seamless. R213 always-mount opacity-gate
|
|
5383
|
+
+ 300ms opacity transition + pointerEvents:none all
|
|
5384
|
+
preserved. data-topo-hub-highlight-radius attr exposes
|
|
5385
|
+
the value for tests. */}
|
|
5386
|
+
{/* Round 386 / Loop: hub-highlight idle opacity 0.9 → 0.95.
|
|
5387
|
+
When workingCount===0 the highlight paints as the visible
|
|
5388
|
+
idle "lamp lit but no work" core (R130 takeover gate).
|
|
5389
|
+
Pre-R386 idle opacity was 0.9 — a ~6 % fade against full
|
|
5390
|
+
paint that read as slightly-dimmed-ghost on the focal
|
|
5391
|
+
point. R386 lifts to 0.95 (idle alpha gap halved 0.10
|
|
5392
|
+
→ 0.05) so the canvas anchor reads more confidently
|
|
5393
|
+
as a present-but-idle state rather than a faded ghost.
|
|
5394
|
+
Theme-consistency / canvas-presence polish family (4th
|
|
5395
|
+
anchor):
|
|
5396
|
+
R370 hub hover-ring opacity 0.7 → 0.8 cyber
|
|
5397
|
+
R371 edge-badge rest opacity 0.82 → 0.85 cyber
|
|
5398
|
+
R372 minimap offline-dot opacity 0.5 → 0.6
|
|
5399
|
+
R386 hub-highlight idle opacity 0.9 → 0.95 (this round)
|
|
5400
|
+
opacity=0 when working preserved so the hub-digit's
|
|
5401
|
+
R130 takeover stays seamless. 300ms opacity transition
|
|
5402
|
+
+ R213 always-mount opacity-gate + pointerEvents:none
|
|
5403
|
+
+ R365 r=5.5 all preserved. data-topo-hub-highlight-
|
|
5404
|
+
opacity attr exposes the resolved value for tests. */}
|
|
4901
5405
|
<circle
|
|
4902
|
-
cx={cx} cy={cy} r="5"
|
|
5406
|
+
cx={cx} cy={cy} r="5.5"
|
|
4903
5407
|
fill="#d1fae5"
|
|
4904
|
-
opacity={workingCount > 0 ? 0 : 0.
|
|
5408
|
+
opacity={workingCount > 0 ? 0 : 0.95}
|
|
4905
5409
|
data-topo-hub-highlight
|
|
4906
5410
|
data-topo-hub-highlight-visible={workingCount > 0 ? 'false' : 'true'}
|
|
5411
|
+
data-topo-hub-highlight-radius="5.5"
|
|
5412
|
+
data-topo-hub-highlight-opacity={workingCount > 0 ? 0 : 0.95}
|
|
4907
5413
|
style={{
|
|
4908
5414
|
pointerEvents: 'none',
|
|
4909
5415
|
transition: 'opacity 300ms ease-out',
|
|
@@ -4934,15 +5440,46 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
4934
5440
|
R142 group boxes, R143/R144 rows, R164 edge badges,
|
|
4935
5441
|
R177 hub ring). prefers-reduced-motion respected via
|
|
4936
5442
|
R29 globals.css blanket. */}
|
|
5443
|
+
{/* Round 385 / Loop: hub hover-ring strokeWidth 1.5 → 1.75.
|
|
5444
|
+
Sibling visual-weight bump (11th anchor) to R367 edge-
|
|
5445
|
+
badge rest stroke 1 → 1.25. The ring is only visible
|
|
5446
|
+
during hub hover (opacity=0 rest, R177 + R370 control
|
|
5447
|
+
the hover-state alpha) so the change manifests purely
|
|
5448
|
+
as a thicker hover-state ring on the canvas focal
|
|
5449
|
+
point. R177 r 14 → 17 grow + R370 opacity 0 → 0.8
|
|
5450
|
+
already lift the hover cue; R385 adds stroke weight
|
|
5451
|
+
as the third lift axis. Stays clear of R51 overlap-
|
|
5452
|
+
test sentinel value 3 (1.75 is non-sentinel); the
|
|
5453
|
+
R51 selector is gated to g[data-node] ancestors so
|
|
5454
|
+
this hub-internal circle is invisible to the probe
|
|
5455
|
+
regardless. R253 stroke transition + pointerEvents:
|
|
5456
|
+
none preserved. data-topo-hub-hover-ring-stroke-width
|
|
5457
|
+
attr exposes the value for tests. */}
|
|
4937
5458
|
<circle
|
|
4938
5459
|
cx={cx} cy={cy}
|
|
4939
5460
|
r={hoveredHub ? 17 : 14}
|
|
4940
5461
|
fill="none"
|
|
4941
5462
|
stroke={isLight ? '#059669' : '#10b981'}
|
|
4942
|
-
strokeWidth="1.
|
|
4943
|
-
|
|
5463
|
+
strokeWidth="1.75"
|
|
5464
|
+
/* Round 370 / Loop: hub hover-ring cyber opacity 0.7 →
|
|
5465
|
+
0.8. R177 designed the hub hover-ring at opacity-0 →
|
|
5466
|
+
0.85 (light) / 0 → 0.7 (cyber). The 15 % gap between
|
|
5467
|
+
the two themes meant cyber-theme operators got a
|
|
5468
|
+
noticeably softer hover cue than light-theme users
|
|
5469
|
+
against backgrounds that should equalise (dark bg
|
|
5470
|
+
needs more luminance to read as 'on'). R370 bumps
|
|
5471
|
+
cyber 0.7 → 0.8, narrowing the theme gap to 5 % —
|
|
5472
|
+
sibling theme-consistency polish to R251 edge badge
|
|
5473
|
+
fill/opacity (cyber 0.82 / light 0.95) and R246/R247
|
|
5474
|
+
panel transition families. Light theme 0.85 stays
|
|
5475
|
+
as is (already in the legibility band). data-topo-
|
|
5476
|
+
hub-hover-ring-opacity attr exposes the value for
|
|
5477
|
+
tests. */
|
|
5478
|
+
opacity={hoveredHub ? (isLight ? 0.85 : 0.8) : 0}
|
|
4944
5479
|
data-topo-hub-hover-ring
|
|
4945
5480
|
data-topo-hub-hover-ring-radius={hoveredHub ? 17 : 14}
|
|
5481
|
+
data-topo-hub-hover-ring-stroke-width="1.75"
|
|
5482
|
+
data-topo-hub-hover-ring-opacity={hoveredHub ? (isLight ? 0.85 : 0.8) : 0}
|
|
4946
5483
|
/* Round 253 / Loop: hub hover ring also gets stroke
|
|
4947
5484
|
transition for theme toggle (cyber #10b981 ↔ light
|
|
4948
5485
|
#059669). The opacity + r transitions stay for hover
|
|
@@ -6059,26 +6596,122 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
6059
6596
|
const detailY = pos.y - detailH / 2;
|
|
6060
6597
|
return (
|
|
6061
6598
|
<g transform={`translate(${detailX}, ${detailY})`} data-topo-hover-detail={session.alias} style={{ pointerEvents: 'none' }}>
|
|
6599
|
+
{/* Round 387 / Loop: hover-detail panel cyber backdrop
|
|
6600
|
+
opacity 0.94 → 0.97. The hover-detail card is
|
|
6601
|
+
ALWAYS rendered in active-hover context (it IS
|
|
6602
|
+
the hover product), so it should carry the
|
|
6603
|
+
same backdrop weight as the R348 recent-signal /
|
|
6604
|
+
legend panel HOVER state (which lifts 0.92 →
|
|
6605
|
+
0.97 cyber). Pre-R387 the card sat at 0.94
|
|
6606
|
+
cyber, leaving a 0.03 alpha gap against the
|
|
6607
|
+
R348 panel-hover state — small but visible
|
|
6608
|
+
when the hover-detail floats next to a hovered
|
|
6609
|
+
recent-signal panel. R387 unifies them at 0.97
|
|
6610
|
+
so all active-hover panels paint with the same
|
|
6611
|
+
confident backdrop opacity in cyber. Light
|
|
6612
|
+
stays at 0.98 (already at the strong end —
|
|
6613
|
+
R348 light also stays at 0.97/0.98 max).
|
|
6614
|
+
Theme-consistency / canvas-presence polish
|
|
6615
|
+
family (5th anchor):
|
|
6616
|
+
R370 hub hover-ring opacity 0.7 → 0.8 cyber
|
|
6617
|
+
R371 edge-badge rest opacity 0.82 → 0.85 cyber
|
|
6618
|
+
R372 minimap offline-dot opacity 0.5 → 0.6
|
|
6619
|
+
R386 hub-highlight idle opacity 0.9 → 0.95
|
|
6620
|
+
R387 hover-detail panel opacity 0.94 → 0.97 cyber (this round)
|
|
6621
|
+
data-topo-hover-detail-opacity attr exposes
|
|
6622
|
+
the resolved value for tests. R348 drop-shadow
|
|
6623
|
+
+ rx=8 + stroke=pal.legendAccent + fill=pal.
|
|
6624
|
+
labelBox.fill all preserved. */}
|
|
6625
|
+
{/* Round 390 / Loop: hover-detail card rx 8 → 10.
|
|
6626
|
+
Corner-radius cascade family — the hover-detail
|
|
6627
|
+
card is a panel-tier surface (192×88 floating
|
|
6628
|
+
info card with drop-shadow + stroke), so its
|
|
6629
|
+
corner radius should match the R331 panel tier
|
|
6630
|
+
(rx=10) used by the recent-signal and legend
|
|
6631
|
+
panels. Pre-R390 it shared rx=8 with the R332
|
|
6632
|
+
minimap and R375/R376 segmented-control tier
|
|
6633
|
+
(Layout-toggle, nodeSize, zoom wrappers),
|
|
6634
|
+
which is the "compact chrome control" tier —
|
|
6635
|
+
a tier mismatch for a content-bearing panel.
|
|
6636
|
+
Corner-radius cascade (6 anchors now):
|
|
6637
|
+
R330 canvas rx 12 (root)
|
|
6638
|
+
R331 panels rx 10 (recent-signal, legend)
|
|
6639
|
+
R332 minimap rx 8 (compact chrome)
|
|
6640
|
+
R375 Layout-toggle rx 8 (segmented control)
|
|
6641
|
+
R376 nodeSize/zoom rx 8 (segmented control)
|
|
6642
|
+
R390 hover-detail rx 10 (panel — this round)
|
|
6643
|
+
Pure paint change; no layout shift (rx grows
|
|
6644
|
+
the corner curve INWARD without changing the
|
|
6645
|
+
card's outer bbox). data-topo-hover-detail-
|
|
6646
|
+
rx attr exposes the resolved value for tests.
|
|
6647
|
+
R348 drop-shadow + stroke + R387 opacity all
|
|
6648
|
+
preserved. */}
|
|
6062
6649
|
<rect
|
|
6063
|
-
x="0" y="0" width={detailW} height={detailH} rx="
|
|
6650
|
+
x="0" y="0" width={detailW} height={detailH} rx="10"
|
|
6064
6651
|
fill={pal.labelBox.fill}
|
|
6065
6652
|
stroke={pal.legendAccent}
|
|
6066
|
-
opacity={isLight ? 0.98 : 0.
|
|
6653
|
+
opacity={isLight ? 0.98 : 0.97}
|
|
6654
|
+
data-topo-hover-detail-opacity={isLight ? 0.98 : 0.97}
|
|
6655
|
+
data-topo-hover-detail-rx="10"
|
|
6067
6656
|
style={{ filter: isLight ? 'drop-shadow(0 4px 12px rgba(15,23,42,0.16))' : 'drop-shadow(0 4px 12px rgba(0,0,0,0.6))' }}
|
|
6068
6657
|
/>
|
|
6069
6658
|
<text x="10" y="16" fontSize="9" fontFamily="monospace" fill={pal.legendAccent} fontWeight="700">
|
|
6070
6659
|
{v.id !== 'unknown' ? v.label : '—'}
|
|
6071
6660
|
</text>
|
|
6072
|
-
|
|
6661
|
+
{/* Round 389 / Loop: hover-detail model line (y=32)
|
|
6662
|
+
fontWeight 400 → 600. R388 lifted body lines
|
|
6663
|
+
(runtime/host/task at fontSize=9) to fw=500;
|
|
6664
|
+
R389 closes the typography hierarchy by giving
|
|
6665
|
+
the model name (the dominant subhead text in
|
|
6666
|
+
the card) its own weight tier. Three-tier
|
|
6667
|
+
ladder now reads cleanly:
|
|
6668
|
+
vendor fontSize=9 fw=700 (label badge)
|
|
6669
|
+
model fontSize=10 fw=600 (subhead — this round)
|
|
6670
|
+
body 3× fontSize=9 fw=500 (R388)
|
|
6671
|
+
One tier step per dimension (size + weight)
|
|
6672
|
+
between adjacent levels — classic editorial
|
|
6673
|
+
hierarchy idiom adapted to a 192×88 SVG card.
|
|
6674
|
+
Sibling to the chip-internal-hierarchy arc
|
|
6675
|
+
(R333-R341/R362/R369) which uses fw=600/500
|
|
6676
|
+
for digit/unit pairs; R389 applies the same
|
|
6677
|
+
fw=600 to a content-bearing identity line.
|
|
6678
|
+
data-topo-hover-detail-model-fw attr exposes
|
|
6679
|
+
the resolved value for tests. pal.legendHeadline
|
|
6680
|
+
fill preserved (R389 doesn't touch color). */}
|
|
6681
|
+
<text x="10" y="32" fontSize="10" fontFamily="monospace" fontWeight="600" fill={pal.legendHeadline} data-topo-hover-detail-model-fw="600">
|
|
6073
6682
|
{session.model || 'model · pending'}
|
|
6074
6683
|
</text>
|
|
6075
|
-
|
|
6684
|
+
{/* Round 388 / Loop: hover-detail body lines (the
|
|
6685
|
+
three fontSize=9 lines: runtime, host, task)
|
|
6686
|
+
gain fontWeight=500. Small-text fw lift family
|
|
6687
|
+
(6th anchor) — fontSize 9-10 px text reads
|
|
6688
|
+
consistently bolder at fw=500 than at the
|
|
6689
|
+
default 400 weight at small sizes, especially
|
|
6690
|
+
on the cyber-theme backdrop where stroke-
|
|
6691
|
+
rendering is the limiting factor.
|
|
6692
|
+
Sibling lifts in this family:
|
|
6693
|
+
R363 recent-row alias text 400 → 500
|
|
6694
|
+
R364 legend-row label 400 → 500
|
|
6695
|
+
R366 group-label count tspan 400 → 500
|
|
6696
|
+
R368 +N more flows footer 400 → 500
|
|
6697
|
+
R373 pressure-bar kicker (font-medium)
|
|
6698
|
+
R388 hover-detail body lines 400 → 500 (this round)
|
|
6699
|
+
Tier structure preserved:
|
|
6700
|
+
y=16 vendor (fw=700, headline)
|
|
6701
|
+
y=32 model (fontSize=10, subhead by size)
|
|
6702
|
+
y=48 runtime / y=64 host / y=80 task (body, now fw=500)
|
|
6703
|
+
The y=80 task line keeps opacity=0.7 so its
|
|
6704
|
+
caption-tier identity stays distinct from the
|
|
6705
|
+
y=48 / y=64 body lines despite shared fw.
|
|
6706
|
+
data-topo-hover-detail-body-fw attr exposes
|
|
6707
|
+
the resolved value for tests. */}
|
|
6708
|
+
<text x="10" y="48" fontSize="9" fontFamily="monospace" fontWeight="500" fill={pal.legendText} data-topo-hover-detail-body-fw="500">
|
|
6076
6709
|
{rt ? rt.label : 'runtime · pending'}
|
|
6077
6710
|
</text>
|
|
6078
|
-
<text x="10" y="64" fontSize="9" fontFamily="monospace" fill={pal.legendText}>
|
|
6711
|
+
<text x="10" y="64" fontSize="9" fontFamily="monospace" fontWeight="500" fill={pal.legendText} data-topo-hover-detail-body-fw="500">
|
|
6079
6712
|
host · {session.server || 'unknown'}
|
|
6080
6713
|
</text>
|
|
6081
|
-
<text x="10" y="80" fontSize="9" fontFamily="monospace" fill={pal.legendText} opacity="0.7">
|
|
6714
|
+
<text x="10" y="80" fontSize="9" fontFamily="monospace" fontWeight="500" fill={pal.legendText} opacity="0.7" data-topo-hover-detail-body-fw="500">
|
|
6082
6715
|
{session.task ? truncate(session.task, 28) : 'no recent task'}
|
|
6083
6716
|
</text>
|
|
6084
6717
|
</g>
|
|
@@ -6224,7 +6857,17 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
6224
6857
|
x="0" y="0" width="230" height="88" rx="10"
|
|
6225
6858
|
fill={pal.legendBox.fill}
|
|
6226
6859
|
stroke={pal.legendBox.stroke}
|
|
6227
|
-
|
|
6860
|
+
// Round 348 / Loop: recent-signal panel rect opacity hover-
|
|
6861
|
+
// state bump — joins the panel-hover cue stack (R135 drop-
|
|
6862
|
+
// shadow boost + R345 title letter-spacing tween 0.3 → 0.4
|
|
6863
|
+
// + R266 fill theme-flip). Cyber 0.92 → 0.97, light 0.97 →
|
|
6864
|
+
// 1.0 on hoveredPanel === 'recent'. The panel "solidifies"
|
|
6865
|
+
// on hover — pure paint-level change, geometry-safe (bbox
|
|
6866
|
+
// unchanged so topo-overlap-test invariants hold). The
|
|
6867
|
+
// R247 transition list already includes `opacity 200ms
|
|
6868
|
+
// ease-out` so the value tween is automatic. Sibling
|
|
6869
|
+
// change at legend panel rect below (~line 7222).
|
|
6870
|
+
opacity={hoveredPanel === 'recent' ? (isLight ? 1 : 0.97) : (isLight ? 0.97 : 0.92)}
|
|
6228
6871
|
style={{
|
|
6229
6872
|
/* R135: drop-shadow intensifies on panel hover. Base
|
|
6230
6873
|
shadow (2px / 6px blur) signals card elevation
|
|
@@ -6276,7 +6919,17 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
6276
6919
|
title (line ~6556) — both panels share the same
|
|
6277
6920
|
editorial-text-spacing convention. data-recent-panel-
|
|
6278
6921
|
title handle unchanged so R266 test still resolves. */}
|
|
6279
|
-
|
|
6922
|
+
{/* Round 345 / Loop: recent-signal panel title gains
|
|
6923
|
+
letter-spacing tween 0.3 → 0.4 on panel hover.
|
|
6924
|
+
hoveredPanel === 'recent' is set by the panel <g>
|
|
6925
|
+
wrapper's onMouseEnter (line ~6263 area). Sibling to
|
|
6926
|
+
R344 hover-letter-spacing applied to the +N more
|
|
6927
|
+
flows footer — same gesture vocabulary at a panel-
|
|
6928
|
+
title scope: hovering the panel chrome spreads the
|
|
6929
|
+
title 0.1 px, signalling "this is a coherent unit
|
|
6930
|
+
you're entering". transition list extends letter-
|
|
6931
|
+
spacing 200ms ease-out alongside existing fill 200ms. */}
|
|
6932
|
+
<text x="13" y="21" fill={pal.legendHeadline} fontSize="12" fontFamily="monospace" fontWeight="700" letterSpacing={hoveredPanel === 'recent' ? '0.4' : '0.3'} style={{ transition: 'fill 200ms ease-out, letter-spacing 200ms ease-out' }} data-recent-panel-title>recent signal</text>
|
|
6280
6933
|
{/* R96: header count now matches what the rows show. Pre-R96
|
|
6281
6934
|
this read "X msgs" off the raw messages array, but the
|
|
6282
6935
|
rows below render DEDUPED flowLinks — so a fleet with 10
|
|
@@ -6337,8 +6990,8 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
6337
6990
|
const alpha = ageSec <= 30
|
|
6338
6991
|
? 1
|
|
6339
6992
|
: ageSec <= 300
|
|
6340
|
-
? 1 - ((ageSec - 30) / 270) * 0.
|
|
6341
|
-
: 0.25
|
|
6993
|
+
? 1 - ((ageSec - 30) / 270) * 0.70 /* R358: floor 0.25 → 0.30 lift across 3 freshness scopes */
|
|
6994
|
+
: 0.30; /* R358: stale floor lifted 0.25 → 0.30 — 20% legibility bump while preserving fresh/stale ratio */
|
|
6342
6995
|
// Dark cyan-400 / light teal-600 with alpha — same
|
|
6343
6996
|
// palette as R161's chip bullet so the two scopes
|
|
6344
6997
|
// visually align even side-by-side.
|
|
@@ -6351,6 +7004,21 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
6351
7004
|
textAnchor="end"
|
|
6352
7005
|
fontSize="10"
|
|
6353
7006
|
fontFamily="monospace"
|
|
7007
|
+
// Round 349 / Loop: editorial letter-spacing 0.2 on the
|
|
7008
|
+
// recent-signal panel header count. Sits one tier below
|
|
7009
|
+
// the R301 panel title letterSpacing="0.3" so the panel
|
|
7010
|
+
// header reads as a 2-step hierarchy (title 0.3 / count
|
|
7011
|
+
// 0.2). Sibling change on the legend panel count below
|
|
7012
|
+
// closes the panel-pair editorial symmetry. Joins the
|
|
7013
|
+
// R285 / R289 / R301 / R302 / R304 / R325 editorial-
|
|
7014
|
+
// letterspacing tier at the panel-summary scope. The
|
|
7015
|
+
// R162 freshness fill, R225 tabular-nums, R311 fw=600,
|
|
7016
|
+
// R336 unit-tspan opacity-0.7 split all preserved —
|
|
7017
|
+
// the tier propagates to all descendant tspans via
|
|
7018
|
+
// SVG inheritance. data-recent-panel-count-letter-
|
|
7019
|
+
// spacing exposes the value for tests.
|
|
7020
|
+
letterSpacing="0.2"
|
|
7021
|
+
data-recent-panel-count-letter-spacing="0.2"
|
|
6354
7022
|
>
|
|
6355
7023
|
{/* Round 225 / Loop: tabular-nums on the panel-header
|
|
6356
7024
|
flow-count tspan. The "{N} flows" string lives in
|
|
@@ -6819,17 +7487,58 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
6819
7487
|
const alpha = ageSec <= 30
|
|
6820
7488
|
? 1
|
|
6821
7489
|
: ageSec <= 300
|
|
6822
|
-
? 1 - ((ageSec - 30) / 270) * 0.
|
|
6823
|
-
: 0.25
|
|
7490
|
+
? 1 - ((ageSec - 30) / 270) * 0.70 /* R358: floor 0.25 → 0.30 lift across 3 freshness scopes */
|
|
7491
|
+
: 0.30; /* R358: stale floor lifted 0.25 → 0.30 — 20% legibility bump while preserving fresh/stale ratio */
|
|
6824
7492
|
return (
|
|
6825
7493
|
<circle
|
|
6826
7494
|
cx={10}
|
|
6827
7495
|
cy={38 + index * 16 - 3}
|
|
6828
|
-
|
|
7496
|
+
/* Round 359 / Loop: recency pip base radius
|
|
7497
|
+
1.6 → 1.8. Sibling lift to R358's freshness-
|
|
7498
|
+
floor bump (alpha 0.25 → 0.30) — pre-R358/
|
|
7499
|
+
R359 the stale pip painted at r=1.6 + α=0.25
|
|
7500
|
+
which read as near-invisible chrome. R358
|
|
7501
|
+
gave it more alpha; R359 gives it more area
|
|
7502
|
+
(1.8² / 1.6² ≈ 1.27, so ~27 % more glyph)
|
|
7503
|
+
so the pip stays distinguishable across the
|
|
7504
|
+
freshness ramp. Geometry: 1.8-radius dot
|
|
7505
|
+
centred at (10, row_y - 3) is bbox 3.6×3.6,
|
|
7506
|
+
still well inside the 7-px left margin
|
|
7507
|
+
(x=6 rect-start → x=13 text-start) the R160
|
|
7508
|
+
pip was placed in. Overlap-test reads the
|
|
7509
|
+
parent row rect's bbox, not this pip's, so
|
|
7510
|
+
grid+ring invariants hold. Matches the same
|
|
7511
|
+
1.6 → 1.8 visual-weight bump R295 applied
|
|
7512
|
+
to the legend swatch (5.5 → 6 base radius)
|
|
7513
|
+
and R287 to the minimap viewport stroke
|
|
7514
|
+
(1 → 1.5). data-recent-row-freshness-radius
|
|
7515
|
+
attr exposes the value for tests. */
|
|
7516
|
+
/* Round 383 / Loop: recency pip base radius
|
|
7517
|
+
1.8 → 2.0. Continues the R359 lift
|
|
7518
|
+
trajectory — pip area grows ~23 % (π·2²/
|
|
7519
|
+
π·1.8² ≈ 1.23) for a clearer at-a-glance
|
|
7520
|
+
freshness anchor in each row. Bbox 4.0×4.0
|
|
7521
|
+
still inside the 7-px R160 left margin
|
|
7522
|
+
(3-px remaining clearance vs 3.4 at r=1.8
|
|
7523
|
+
— geometry-safe margin holds). Sibling
|
|
7524
|
+
visual-weight bump family (9th anchor now):
|
|
7525
|
+
R287 minimap viewport stroke 1 → 1.5
|
|
7526
|
+
R295 legend swatch base radius 5.5 → 6
|
|
7527
|
+
R359 recent-row pip base radius 1.6 → 1.8
|
|
7528
|
+
R360 hub digit fontSize 11 → 12
|
|
7529
|
+
R361 edge-badge digit fontSize 10 → 11
|
|
7530
|
+
R365 hub-highlight base radius 5 → 5.5
|
|
7531
|
+
R367 edge-badge rest stroke 1 → 1.25
|
|
7532
|
+
R374 pressure-bar height 1.5 → 2
|
|
7533
|
+
R383 recent-row pip radius 1.8 → 2.0 (this round)
|
|
7534
|
+
data-recent-row-freshness-radius attr
|
|
7535
|
+
bumps to '2.0' for tests. */
|
|
7536
|
+
r={2.0}
|
|
6829
7537
|
fill={pal.legendAccent}
|
|
6830
7538
|
opacity={alpha}
|
|
6831
7539
|
data-recent-row-freshness={link.key}
|
|
6832
7540
|
data-recent-row-freshness-alpha={alpha.toFixed(2)}
|
|
7541
|
+
data-recent-row-freshness-radius="2.0"
|
|
6833
7542
|
style={{ pointerEvents: 'none', transition: 'opacity 200ms ease-out' }}
|
|
6834
7543
|
/>
|
|
6835
7544
|
);
|
|
@@ -6859,8 +7568,28 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
6859
7568
|
fill={isRowActive ? pal.legendHeadline : pal.legendText}
|
|
6860
7569
|
fontSize="9"
|
|
6861
7570
|
fontFamily="monospace"
|
|
7571
|
+
/* Round 363 / Loop: recent-row text fontWeight 400
|
|
7572
|
+
→ 500 (font-medium tier). At fontSize=9 the
|
|
7573
|
+
default-weight 400 glyphs read thin against the
|
|
7574
|
+
panel chrome (pal.legendBox.fill with 0.92/0.97
|
|
7575
|
+
opacity); the 100-weight bump lifts the alias→
|
|
7576
|
+
alias text into the legibility band without
|
|
7577
|
+
changing geometry. The R320 count tspan fw=600
|
|
7578
|
+
(cold) / fw=700 (hot) override still wins
|
|
7579
|
+
locally via inline fontWeight on the inner
|
|
7580
|
+
tspan, so the count-vs-alias hierarchy stays
|
|
7581
|
+
intact:
|
|
7582
|
+
alias fw 500 (R363, this round)
|
|
7583
|
+
count fw 600/700 (R320)
|
|
7584
|
+
Sibling typography lift to R362 chip-row digit
|
|
7585
|
+
500 → 600 — both nudge a within-element data
|
|
7586
|
+
tier without disturbing the surrounding family
|
|
7587
|
+
baseline. data-recent-row-text-font-weight attr
|
|
7588
|
+
exposes the value for tests. */
|
|
7589
|
+
fontWeight="500"
|
|
6862
7590
|
data-recent-row-text={link.key}
|
|
6863
7591
|
data-recent-row-text-pinned={isRowPinned ? 'true' : 'false'}
|
|
7592
|
+
data-recent-row-text-font-weight="500"
|
|
6864
7593
|
style={{
|
|
6865
7594
|
transition: 'fill 150ms ease-out, letter-spacing 150ms ease-out',
|
|
6866
7595
|
letterSpacing: isRowPinned ? '0.5px' : '0px',
|
|
@@ -7122,6 +7851,23 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
7122
7851
|
surface. transition list extends letter-spacing
|
|
7123
7852
|
200ms ease-out alongside the existing opacity/
|
|
7124
7853
|
fill easings. */}
|
|
7854
|
+
{/* Round 368 / Loop: `+N more flows` footer text gains
|
|
7855
|
+
fontWeight=500 (font-medium tier). Sibling small-
|
|
7856
|
+
text fw lift family with R363 recent-row alias
|
|
7857
|
+
+ R364 legend-row label + R366 group-label count
|
|
7858
|
+
— all four lifts share the same theory: at small
|
|
7859
|
+
fontSize (9-11 px) against panel chrome, SVG-
|
|
7860
|
+
default fw 400 sits at the legibility floor;
|
|
7861
|
+
fw 500 brings the glyph into the deliberate-data
|
|
7862
|
+
band. fontStyle=italic + opacity 0.55 rest + R325
|
|
7863
|
+
letterSpacing 0.2 baseline + R344 hover-spread
|
|
7864
|
+
0.2 → 0.3 + R195 cyan fill on hover all preserved
|
|
7865
|
+
— the fw bump just thickens the italic stroke.
|
|
7866
|
+
Hover-state punch (R195 fill + R325 opacity 0.55
|
|
7867
|
+
→ 0.85 + R344 letter-spacing + R133 underline)
|
|
7868
|
+
stays as is, so the rest-vs-hover delta still
|
|
7869
|
+
reads clearly. data-recent-panel-more-font-weight
|
|
7870
|
+
attr exposes the value for tests. */}
|
|
7125
7871
|
<text
|
|
7126
7872
|
x="115" y="82"
|
|
7127
7873
|
textAnchor="middle"
|
|
@@ -7129,11 +7875,13 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
7129
7875
|
fontSize="9"
|
|
7130
7876
|
fontFamily="monospace"
|
|
7131
7877
|
fontStyle="italic"
|
|
7878
|
+
fontWeight="500"
|
|
7132
7879
|
letterSpacing={hoveredRecentMore ? '0.3' : '0.2'}
|
|
7133
7880
|
opacity={hoveredRecentMore ? 0.85 : 0.55}
|
|
7134
7881
|
textDecoration={hoveredRecentMore ? 'underline' : 'none'}
|
|
7135
7882
|
data-recent-panel-more={moreCount}
|
|
7136
7883
|
data-recent-panel-more-hovered={hoveredRecentMore ? 'true' : 'false'}
|
|
7884
|
+
data-recent-panel-more-font-weight="500"
|
|
7137
7885
|
style={{ transition: 'opacity 150ms ease-out, fill 200ms ease-out, letter-spacing 200ms ease-out' }}
|
|
7138
7886
|
>
|
|
7139
7887
|
{`+ ${moreCount}`}
|
|
@@ -7186,7 +7934,12 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
7186
7934
|
x="0" y="0" width="224" height="88" rx="10"
|
|
7187
7935
|
fill={pal.legendBox.fill}
|
|
7188
7936
|
stroke={pal.legendBox.stroke}
|
|
7189
|
-
|
|
7937
|
+
// R348 sibling — legend panel rect opacity hover-state
|
|
7938
|
+
// bump 0.92 → 0.97 (cyber) / 0.97 → 1 (light) on
|
|
7939
|
+
// hoveredPanel === 'legend'. Pairs with the recent-signal
|
|
7940
|
+
// panel rect above so the two corner panels' hover cues
|
|
7941
|
+
// stay symmetric. Geometry-safe (paint-only).
|
|
7942
|
+
opacity={hoveredPanel === 'legend' ? (isLight ? 1 : 0.97) : (isLight ? 0.97 : 0.92)}
|
|
7190
7943
|
style={{
|
|
7191
7944
|
filter: hoveredPanel === 'legend'
|
|
7192
7945
|
? (isLight ? 'drop-shadow(0 4px 12px rgba(15,23,42,0.14))'
|
|
@@ -7209,7 +7962,9 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
7209
7962
|
surrounding chrome eased; R266 closes both at once. */}
|
|
7210
7963
|
{/* R301: sibling to recent-signal panel title above —
|
|
7211
7964
|
same letterSpacing 0.3 for editorial parity. */}
|
|
7212
|
-
|
|
7965
|
+
{/* R345 sibling — legend panel title same hover letter-
|
|
7966
|
+
spacing tween 0.3 → 0.4 on panel hover. */}
|
|
7967
|
+
<text x="13" y="21" fill={pal.legendHeadline} fontSize="12" fontFamily="monospace" fontWeight="700" letterSpacing={hoveredPanel === 'legend' ? '0.4' : '0.3'} style={{ transition: 'fill 200ms ease-out, letter-spacing 200ms ease-out' }} data-legend-panel-title>legend</text>
|
|
7213
7968
|
{/* Round 257 / Loop: legend panel header count picks up the
|
|
7214
7969
|
symmetric 13L/13R inner-padding pattern from the recent-
|
|
7215
7970
|
signal panel. Pre-R257 the legend header was 13px from
|
|
@@ -7273,7 +8028,14 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
7273
8028
|
<text
|
|
7274
8029
|
x="211" y="21" textAnchor="end"
|
|
7275
8030
|
fill={pal.legendAccent} fontSize="10" fontFamily="monospace" fontWeight="600"
|
|
8031
|
+
// R349 sibling — legend panel header count picks up
|
|
8032
|
+
// letterSpacing="0.2", one tier below the R301 panel
|
|
8033
|
+
// title 0.3. Pairs with the recent-signal panel count
|
|
8034
|
+
// letter-spacing above so the two corner panels' header
|
|
8035
|
+
// typography stays editorially symmetric.
|
|
8036
|
+
letterSpacing="0.2"
|
|
7276
8037
|
data-legend-panel-count
|
|
8038
|
+
data-legend-panel-count-letter-spacing="0.2"
|
|
7277
8039
|
style={{
|
|
7278
8040
|
transition: 'fill 200ms ease-out',
|
|
7279
8041
|
fontVariantNumeric: 'tabular-nums',
|
|
@@ -7528,8 +8290,31 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
7528
8290
|
fill={hoveredStatus === row.key || isPinned ? pal.legendHeadline : pal.legendText}
|
|
7529
8291
|
fontSize="11"
|
|
7530
8292
|
fontFamily="monospace"
|
|
8293
|
+
/* Round 364 / Loop: legend-row label fontWeight 400
|
|
8294
|
+
→ 500. Sibling typography lift to R363 recent-row
|
|
8295
|
+
text fw 400 → 500. Both surfaces render small
|
|
8296
|
+
monospace text against panel chrome at fontSize
|
|
8297
|
+
9-11 where SVG-default fw 400 sits at the
|
|
8298
|
+
legibility floor. font-medium tier (500) gives
|
|
8299
|
+
the label a more deliberate-data register.
|
|
8300
|
+
The R309 per-row count text (separate element
|
|
8301
|
+
below at x=215 textAnchor=end) keeps its own
|
|
8302
|
+
fontWeight 600 inline override, so the count >
|
|
8303
|
+
label hierarchy stays intact at the legend
|
|
8304
|
+
scope same as R363 holds it at the recent-row
|
|
8305
|
+
scope:
|
|
8306
|
+
legend label fw 500 (R364, this round)
|
|
8307
|
+
legend count fw 600 (R309)
|
|
8308
|
+
recent alias fw 500 (R363)
|
|
8309
|
+
recent count fw 600/700 (R320)
|
|
8310
|
+
data-legend-row-label-font-weight attr exposes
|
|
8311
|
+
the value for tests. R219 letter-spacing pin
|
|
8312
|
+
tween + R55 fill transition + R181 always-mount
|
|
8313
|
+
pin ring all preserved. */
|
|
8314
|
+
fontWeight="500"
|
|
7531
8315
|
data-legend-row-label={row.key}
|
|
7532
8316
|
data-legend-row-label-pinned={isPinned ? 'true' : 'false'}
|
|
8317
|
+
data-legend-row-label-font-weight="500"
|
|
7533
8318
|
style={{
|
|
7534
8319
|
transition: 'fill 150ms ease-out, letter-spacing 150ms ease-out',
|
|
7535
8320
|
letterSpacing: isPinned ? '0.5px' : '0px',
|
|
@@ -7815,7 +8600,13 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
7815
8600
|
resetView();
|
|
7816
8601
|
}
|
|
7817
8602
|
}}
|
|
8603
|
+
// R346: viewport rect hover affordance driven by parent.
|
|
8604
|
+
onMouseEnter={() => setHoveredMinimap(true)}
|
|
8605
|
+
onMouseLeave={() => setHoveredMinimap(false)}
|
|
8606
|
+
onFocus={() => setHoveredMinimap(true)}
|
|
8607
|
+
onBlur={() => setHoveredMinimap(false)}
|
|
7818
8608
|
data-topo-minimap
|
|
8609
|
+
data-topo-minimap-hovered={hoveredMinimap ? 'true' : 'false'}
|
|
7819
8610
|
>
|
|
7820
8611
|
<svg width={MW} height={MH} viewBox={`0 0 ${MW} ${MH}`} style={{ display: 'block' }}>
|
|
7821
8612
|
{/* Round 198 / Loop: minimap dots gain smooth status
|
|
@@ -7842,14 +8633,48 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
7842
8633
|
const isOn = s.status !== 'offline' || !!sseN;
|
|
7843
8634
|
const st = nodeStatus(s, isOn, isLight);
|
|
7844
8635
|
return (
|
|
8636
|
+
/* Round 372 / Loop: minimap offline-dot opacity
|
|
8637
|
+
0.5 → 0.6. Sibling stale-state legibility lift
|
|
8638
|
+
to R358 freshness ramp floor 0.25 → 0.30 + R317
|
|
8639
|
+
subordinate-text-lift family. Pre-R372 R198
|
|
8640
|
+
drew offline dots at α=0.5 (44 % below online
|
|
8641
|
+
0.9). The minimap is a small overlay against
|
|
8642
|
+
the canvas backdrop — at α=0.5 offline dots
|
|
8643
|
+
sat at the legibility floor when the minimap
|
|
8644
|
+
mounted (only on non-default view). R372 lifts
|
|
8645
|
+
offline 0.5 → 0.6 for +20 % relative presence;
|
|
8646
|
+
online stays at 0.9 so the offline/online
|
|
8647
|
+
contrast ratio is now 0.6/0.9 ≈ 0.67 (vs prior
|
|
8648
|
+
0.5/0.9 ≈ 0.56) — still a clear two-tier
|
|
8649
|
+
distinction. R198 opacity + fill + r transition
|
|
8650
|
+
list preserved so status flips still ease
|
|
8651
|
+
smoothly. data-topo-minimap-dot-opacity attr
|
|
8652
|
+
exposes the resolved value for tests. */
|
|
7845
8653
|
<circle
|
|
7846
8654
|
key={s.alias}
|
|
7847
8655
|
cx={p.x * sx} cy={p.y * sy}
|
|
7848
|
-
|
|
8656
|
+
/* Round 384 / Loop: minimap online dot radius 1.7
|
|
8657
|
+
→ 1.9. Sibling visual-weight bump (10th anchor)
|
|
8658
|
+
to R383 recent-row pip 1.8 → 2.0. R198 designed
|
|
8659
|
+
the dots at 1.7 (online) / 1.2 (offline) — at
|
|
8660
|
+
the minimap's 120 × 82 scale these read clearly
|
|
8661
|
+
but the online ↔ offline contrast was modest
|
|
8662
|
+
(1.7/1.2 = 1.42×). R384 bumps online to 1.9 so
|
|
8663
|
+
the tier delta widens to 1.58× (1.9/1.2). Pair
|
|
8664
|
+
completes minimap-dot legibility polish:
|
|
8665
|
+
R358 (era R372) offline opacity 0.5 → 0.6
|
|
8666
|
+
R384 online radius 1.7 → 1.9 (this round)
|
|
8667
|
+
R198 transition list (opacity + fill + r 200ms)
|
|
8668
|
+
preserved so status flips still ease smoothly.
|
|
8669
|
+
data-topo-minimap-dot-radius attr exposes the
|
|
8670
|
+
resolved value for tests. */
|
|
8671
|
+
r={isOn ? 1.9 : 1.2}
|
|
7849
8672
|
fill={st.primary}
|
|
7850
|
-
opacity={isOn ? 0.9 : 0.
|
|
8673
|
+
opacity={isOn ? 0.9 : 0.6}
|
|
7851
8674
|
data-topo-minimap-dot={s.alias}
|
|
7852
8675
|
data-topo-minimap-dot-online={isOn ? 'true' : 'false'}
|
|
8676
|
+
data-topo-minimap-dot-opacity={isOn ? 0.9 : 0.6}
|
|
8677
|
+
data-topo-minimap-dot-radius={isOn ? 1.9 : 1.2}
|
|
7853
8678
|
style={{
|
|
7854
8679
|
transition: 'opacity 200ms ease-out, fill 200ms ease-out, r 200ms ease-out',
|
|
7855
8680
|
} as React.CSSProperties}
|
|
@@ -7890,17 +8715,40 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
7890
8715
|
information element to lift it above ambient
|
|
7891
8716
|
chrome. opacity 0.9 stays — strokeWidth alone
|
|
7892
8717
|
does the lifting. */}
|
|
8718
|
+
{/* Round 379 / Loop: minimap viewport rect picks up
|
|
8719
|
+
strokeLinejoin='round'. Pre-R379 the rect's 4
|
|
8720
|
+
corners painted with default 'miter' joins —
|
|
8721
|
+
sharp 90° corners with a small miter overshoot
|
|
8722
|
+
(≈ strokeWidth × 1.4 = 2.1 px at sw=1.5). R379
|
|
8723
|
+
rounds the joins so corners arc smoothly through
|
|
8724
|
+
a quarter-circle of radius ≈ strokeWidth/2. At
|
|
8725
|
+
sw=1.5 that's a 0.75-px radius — subtle but
|
|
8726
|
+
matches the same stroke-softening vocabulary R288
|
|
8727
|
+
chrome icons (zoom/reset/fullscreen) and R378
|
|
8728
|
+
flow-rail already speak. Geometry-safe: stroke-
|
|
8729
|
+
linejoin only affects the corner overshoot, the
|
|
8730
|
+
rect's bbox is unchanged. R287 strokeWidth=1.5 +
|
|
8731
|
+
R346 hover-state strokeWidth/opacity bump + R199
|
|
8732
|
+
smoothView x/y/w/h transition all preserved.
|
|
8733
|
+
data-topo-minimap-viewport-linejoin attr exposes
|
|
8734
|
+
the value for tests. */}
|
|
7893
8735
|
<rect
|
|
7894
8736
|
x={Math.max(0, rectX)} y={Math.max(0, rectY)}
|
|
7895
8737
|
width={Math.max(0, Math.min(MW - Math.max(0, rectX), rectW))}
|
|
7896
8738
|
height={Math.max(0, Math.min(MH - Math.max(0, rectY), rectH))}
|
|
7897
|
-
fill="none" stroke={pal.legendAccent}
|
|
8739
|
+
fill="none" stroke={pal.legendAccent}
|
|
8740
|
+
// R346: strokeWidth + opacity tween on container hover.
|
|
8741
|
+
strokeWidth={hoveredMinimap ? '1.75' : '1.5'}
|
|
8742
|
+
strokeLinejoin="round"
|
|
8743
|
+
opacity={hoveredMinimap ? '1' : '0.9'}
|
|
7898
8744
|
data-topo-minimap-viewport
|
|
7899
8745
|
data-topo-minimap-viewport-smooth={smoothView ? 'true' : 'false'}
|
|
8746
|
+
data-topo-minimap-viewport-hover={hoveredMinimap ? 'true' : 'false'}
|
|
8747
|
+
data-topo-minimap-viewport-linejoin="round"
|
|
7900
8748
|
style={{
|
|
7901
8749
|
transition: smoothView
|
|
7902
|
-
? 'x 280ms ease-out, y 280ms ease-out, width 280ms ease-out, height 280ms ease-out'
|
|
7903
|
-
: '
|
|
8750
|
+
? 'x 280ms ease-out, y 280ms ease-out, width 280ms ease-out, height 280ms ease-out, stroke-width 200ms ease-out, opacity 200ms ease-out'
|
|
8751
|
+
: 'stroke-width 200ms ease-out, opacity 200ms ease-out',
|
|
7904
8752
|
} as React.CSSProperties}
|
|
7905
8753
|
/>
|
|
7906
8754
|
</svg>
|
|
@@ -7955,8 +8803,22 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
7955
8803
|
own transition-colors. Same R254 holdover pattern that
|
|
7956
8804
|
R263 just closed at the canvas wrapper scope, now at the
|
|
7957
8805
|
chrome strip's nodeSize sub-wrapper scope. */}
|
|
8806
|
+
{/* Round 376 / Loop: nodeSize wrapper rounded-md → rounded-lg.
|
|
8807
|
+
Sibling polish to R375 Layout-toggle wrapper. Three
|
|
8808
|
+
chrome-strip segmented controls now all share rounded-lg
|
|
8809
|
+
at the wrapper tier:
|
|
8810
|
+
R375 Layout-toggle wrapper rounded-lg 8 px
|
|
8811
|
+
R376 nodeSize wrapper rounded-lg 8 px (this round)
|
|
8812
|
+
R376 zoom wrapper rounded-lg 8 px (this round)
|
|
8813
|
+
Individual atomic chrome buttons (reset, fullscreen) keep
|
|
8814
|
+
rounded-md (6 px) as their own atomic-button tier — the
|
|
8815
|
+
chrome strip's typography now expresses a clear two-tier
|
|
8816
|
+
hierarchy: 'segmented control container' (rounded-lg)
|
|
8817
|
+
vs 'standalone button' (rounded-md). Pure paint change,
|
|
8818
|
+
no layout shift. */}
|
|
7958
8819
|
<div
|
|
7959
|
-
className="flex items-center rounded-
|
|
8820
|
+
className="flex items-center rounded-lg border overflow-hidden"
|
|
8821
|
+
data-topo-chrome-nodesize-radius="rounded-lg"
|
|
7960
8822
|
style={{
|
|
7961
8823
|
background: pal.legendBox.fill,
|
|
7962
8824
|
borderColor: pal.containerBorder,
|
|
@@ -8036,8 +8898,12 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
8036
8898
|
data-topo-chrome-view-group-leader marks the boundary surface
|
|
8037
8899
|
for the test probe; data-topo-chrome-fleet-group-trailer marks
|
|
8038
8900
|
the nodeSize wrapper's right edge for the gap measurement. */}
|
|
8901
|
+
{/* R376 sibling — zoom wrapper rounded-md → rounded-lg.
|
|
8902
|
+
Closes the chrome-strip segmented-control corner radius
|
|
8903
|
+
cascade (Layout R375 + nodeSize R376 + zoom R376). */}
|
|
8039
8904
|
<div
|
|
8040
|
-
className="ml-1.5 flex items-center rounded-
|
|
8905
|
+
className="ml-1.5 flex items-center rounded-lg border overflow-hidden"
|
|
8906
|
+
data-topo-chrome-zoom-wrapper-radius="rounded-lg"
|
|
8041
8907
|
style={{
|
|
8042
8908
|
background: pal.legendBox.fill,
|
|
8043
8909
|
borderColor: pal.containerBorder,
|
|
@@ -8053,7 +8919,8 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
8053
8919
|
// R196: press-state deepens bg one tier above hover (white/5
|
|
8054
8920
|
// → white/10) so mouse-down has a tactile dim before the
|
|
8055
8921
|
// R186 icon pop fires on release.
|
|
8056
|
-
|
|
8922
|
+
// R352: `group` lets the inner svg respond via group-hover.
|
|
8923
|
+
className="group px-2 py-1 hover:bg-white/5 active:bg-white/10 transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-cyan-400/60 focus-visible:ring-inset"
|
|
8057
8924
|
style={{ color: pal.legendText }}
|
|
8058
8925
|
aria-label="Zoom out"
|
|
8059
8926
|
title="Zoom out (−)"
|
|
@@ -8061,11 +8928,25 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
8061
8928
|
{/* R186: icon pop on click. CSS animation runs once;
|
|
8062
8929
|
React removes the class after 240ms so a quick
|
|
8063
8930
|
re-click can replay. */}
|
|
8931
|
+
{/* Round 352 / Loop: zoom-out icon picks up group-hover:
|
|
8932
|
+
scale-110 — sibling to R350 reset hover-rotate. Pre-
|
|
8933
|
+
R352 hovering the zoom button only changed the bg
|
|
8934
|
+
(white/5); the icon inside stayed perfectly still.
|
|
8935
|
+
R352 lifts the icon 10% on hover for a tactile "this
|
|
8936
|
+
button does something" cue. The R186 anet-chrome-pop
|
|
8937
|
+
keyframe (220ms scale 1→1.06→1) still owns transform
|
|
8938
|
+
during click via CSS-animation precedence over
|
|
8939
|
+
transition-transform; after the pop ends + className
|
|
8940
|
+
is removed, the group-hover scale-110 picks up
|
|
8941
|
+
smoothly. `transform-gpu` hint promotes the svg to
|
|
8942
|
+
its own compositor layer for crisper edges during
|
|
8943
|
+
the scale tween. Sibling change on zoom-in icon
|
|
8944
|
+
below. */}
|
|
8064
8945
|
<svg
|
|
8065
8946
|
width="12" height="12" viewBox="0 0 24 24"
|
|
8066
8947
|
fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round"
|
|
8067
8948
|
aria-hidden
|
|
8068
|
-
className={chromePopping === 'zoom-out' ? 'anet-chrome-pop' :
|
|
8949
|
+
className={`transition-transform duration-200 ease-out group-hover:scale-110 transform-gpu${chromePopping === 'zoom-out' ? ' anet-chrome-pop' : ''}`}
|
|
8069
8950
|
data-topo-chrome-zoom-out-icon
|
|
8070
8951
|
><path d="M5 12h14" /></svg>
|
|
8071
8952
|
</button>
|
|
@@ -8111,11 +8992,17 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
8111
8992
|
chromePopping === 'zoom-in' || chromePopping === 'zoom-out'
|
|
8112
8993
|
? 'true' : 'false'
|
|
8113
8994
|
}
|
|
8995
|
+
data-topo-chrome-zoom-level-hover={hoveredZoomLevel ? 'true' : 'false'}
|
|
8996
|
+
onMouseEnter={() => setHoveredZoomLevel(true)}
|
|
8997
|
+
onMouseLeave={() => setHoveredZoomLevel(false)}
|
|
8114
8998
|
style={{
|
|
8115
8999
|
color: pal.legendText,
|
|
8116
9000
|
borderColor: pal.containerBorder,
|
|
8117
9001
|
minWidth: 46,
|
|
8118
9002
|
display: 'inline-block',
|
|
9003
|
+
// R347: letter-spacing hover tween — extends R344/R345
|
|
9004
|
+
// hover-letter-spacing family into the chrome strip.
|
|
9005
|
+
letterSpacing: hoveredZoomLevel ? '0.5px' : '0',
|
|
8119
9006
|
/* Round 264 / Loop: zoom level readout gains theme-toggle
|
|
8120
9007
|
transition. The span has theme-driven color (pal.
|
|
8121
9008
|
legendText) + border-x (pal.containerBorder via the
|
|
@@ -8124,7 +9011,7 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
8124
9011
|
on theme flip while siblings eased. Sibling treatment
|
|
8125
9012
|
to the nodeSize + zoom wrapper transitions added this
|
|
8126
9013
|
round. */
|
|
8127
|
-
transition: 'color 200ms ease-out, border-color 200ms ease-out',
|
|
9014
|
+
transition: 'color 200ms ease-out, border-color 200ms ease-out, letter-spacing 200ms ease-out',
|
|
8128
9015
|
}}
|
|
8129
9016
|
title="Current zoom level"
|
|
8130
9017
|
>
|
|
@@ -8135,18 +9022,22 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
8135
9022
|
data-topo-chrome-zoom-in
|
|
8136
9023
|
data-topo-chrome-zoom-in-popping={chromePopping === 'zoom-in' ? 'true' : 'false'}
|
|
8137
9024
|
// R196: press-state (mirror of zoom-out above).
|
|
8138
|
-
|
|
9025
|
+
// R352: `group` lets the inner svg respond via group-hover.
|
|
9026
|
+
className="group px-2 py-1 hover:bg-white/5 active:bg-white/10 transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-cyan-400/60 focus-visible:ring-inset"
|
|
8139
9027
|
style={{ color: pal.legendText }}
|
|
8140
9028
|
aria-label="Zoom in"
|
|
8141
9029
|
title="Zoom in (+)"
|
|
8142
9030
|
>
|
|
8143
9031
|
{/* R186: icon pop on click. Same one-shot CSS animation
|
|
8144
9032
|
as zoom-out; React removes the class after 240ms. */}
|
|
9033
|
+
{/* R352 sibling — zoom-in icon picks up the same
|
|
9034
|
+
group-hover:scale-110 family. Mirror change at
|
|
9035
|
+
the zoom-out icon above. */}
|
|
8145
9036
|
<svg
|
|
8146
9037
|
width="12" height="12" viewBox="0 0 24 24"
|
|
8147
9038
|
fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round"
|
|
8148
9039
|
aria-hidden
|
|
8149
|
-
className={chromePopping === 'zoom-in' ? 'anet-chrome-pop' :
|
|
9040
|
+
className={`transition-transform duration-200 ease-out group-hover:scale-110 transform-gpu${chromePopping === 'zoom-in' ? ' anet-chrome-pop' : ''}`}
|
|
8150
9041
|
data-topo-chrome-zoom-in-icon
|
|
8151
9042
|
><path d="M12 5v14M5 12h14" /></svg>
|
|
8152
9043
|
</button>
|
|
@@ -8155,6 +9046,12 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
8155
9046
|
onClick={() => { armResetSpin(); resetView(); }}
|
|
8156
9047
|
data-topo-chrome-reset
|
|
8157
9048
|
data-topo-chrome-reset-spinning={resetSpinning ? 'true' : 'false'}
|
|
9049
|
+
data-topo-chrome-reset-hover={hoveredReset ? 'true' : 'false'}
|
|
9050
|
+
// R350: hover state drives the icon transform below.
|
|
9051
|
+
onMouseEnter={() => setHoveredReset(true)}
|
|
9052
|
+
onMouseLeave={() => setHoveredReset(false)}
|
|
9053
|
+
onFocus={() => setHoveredReset(true)}
|
|
9054
|
+
onBlur={() => setHoveredReset(false)}
|
|
8158
9055
|
// R196: press-state deepens before R184 reset-spin fires on
|
|
8159
9056
|
// release — mouse-down dim then 450ms spin = full handshake.
|
|
8160
9057
|
className="p-1.5 rounded-md border hover:bg-white/5 active:bg-white/10 transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-cyan-400/60"
|
|
@@ -8184,6 +9081,17 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
8184
9081
|
aria-hidden
|
|
8185
9082
|
className={resetSpinning ? 'anet-reset-spin' : undefined}
|
|
8186
9083
|
data-topo-chrome-reset-icon
|
|
9084
|
+
// R350: hover-rotate preview of the R184 click-spin.
|
|
9085
|
+
// Gated on !resetSpinning so the anet-reset-spin keyframe
|
|
9086
|
+
// owns transform during its 450ms run. transformOrigin
|
|
9087
|
+
// 'center' so rotation pivots around the icon's centre
|
|
9088
|
+
// (default would be top-left and the icon would arc).
|
|
9089
|
+
style={{
|
|
9090
|
+
transform: hoveredReset && !resetSpinning ? 'rotate(-8deg)' : 'rotate(0deg)',
|
|
9091
|
+
transformOrigin: 'center',
|
|
9092
|
+
transition: 'transform 200ms ease-out',
|
|
9093
|
+
}}
|
|
9094
|
+
data-topo-chrome-reset-icon-hover={hoveredReset && !resetSpinning ? 'true' : 'false'}
|
|
8187
9095
|
>
|
|
8188
9096
|
<path d="M3 12a9 9 0 1 0 9-9 9 9 0 0 0-6.4 2.6L3 8" />
|
|
8189
9097
|
<path d="M3 3v5h5" />
|
|
@@ -8217,7 +9125,12 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
8217
9125
|
its inactive state benefits from the same "hover previews
|
|
8218
9126
|
active state" idiom R163 designed. Sibling treatment to
|
|
8219
9127
|
the nodeSize buttons at line ~6711. */
|
|
8220
|
-
|
|
9128
|
+
// R353: `group` lets the inner svg respond via group-hover —
|
|
9129
|
+
// sibling to R352 zoom buttons. Closes the chrome-strip per-
|
|
9130
|
+
// icon hover-affordance arc (zoom-out / zoom-in / reset /
|
|
9131
|
+
// fullscreen now all carry an icon-level hover gesture in
|
|
9132
|
+
// addition to the bg hover).
|
|
9133
|
+
className={`group p-1.5 rounded-md border transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-cyan-400/60 ${
|
|
8221
9134
|
isFullscreen
|
|
8222
9135
|
? 'bg-cyan-500/15 text-cyan-300 font-medium hover:bg-cyan-500/20 active:bg-cyan-500/25'
|
|
8223
9136
|
: 'hover:bg-cyan-500/5 active:bg-cyan-500/15'
|
|
@@ -8236,12 +9149,20 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
8236
9149
|
at the reset icon above. data-topo-chrome-fullscreen-
|
|
8237
9150
|
icon attribute exposes BOTH variants (entered / exited)
|
|
8238
9151
|
for the round's stroke-width regression probe. */}
|
|
9152
|
+
{/* Round 353 / Loop: fullscreen icon (both enter + exit
|
|
9153
|
+
variants) picks up the R352 family group-hover:scale-110.
|
|
9154
|
+
Pre-R353 hovering the button only changed the bg; the
|
|
9155
|
+
icon stayed still. R353 lifts the icon 10 % on hover —
|
|
9156
|
+
same gesture vocabulary as the zoom buttons. transform-
|
|
9157
|
+
gpu hint promotes the svg to its own compositor layer
|
|
9158
|
+
for crisper edges during the scale tween. Closes the
|
|
9159
|
+
chrome-strip per-icon hover-affordance arc. */}
|
|
8239
9160
|
{isFullscreen ? (
|
|
8240
|
-
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden data-topo-chrome-fullscreen-icon="exit">
|
|
9161
|
+
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden className="transition-transform duration-200 ease-out group-hover:scale-110 transform-gpu" data-topo-chrome-fullscreen-icon="exit">
|
|
8241
9162
|
<path d="M8 3v4a1 1 0 0 1-1 1H3M21 8h-4a1 1 0 0 1-1-1V3M3 16h4a1 1 0 0 1 1 1v4M16 21v-4a1 1 0 0 1 1-1h4" />
|
|
8242
9163
|
</svg>
|
|
8243
9164
|
) : (
|
|
8244
|
-
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden data-topo-chrome-fullscreen-icon="enter">
|
|
9165
|
+
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden className="transition-transform duration-200 ease-out group-hover:scale-110 transform-gpu" data-topo-chrome-fullscreen-icon="enter">
|
|
8245
9166
|
<path d="M3 8V5a2 2 0 0 1 2-2h3M21 8V5a2 2 0 0 0-2-2h-3M3 16v3a2 2 0 0 0 2 2h3M21 16v3a2 2 0 0 1-2 2h-3" />
|
|
8246
9167
|
</svg>
|
|
8247
9168
|
)}
|