From 3544fe972f7640c191521345a9f02b4b61e9311e Mon Sep 17 00:00:00 2001 From: adibarra <93070681+adibarra@users.noreply.github.com> Date: Mon, 22 Jun 2026 22:55:08 -0500 Subject: [PATCH] fix(inference): apply quick-filter URL selections after mount to avoid pill hydration desync --- .../components/inference/InferenceContext.tsx | 43 +++++++++++-------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/packages/app/src/components/inference/InferenceContext.tsx b/packages/app/src/components/inference/InferenceContext.tsx index 56d8c602..796a8eed 100644 --- a/packages/app/src/components/inference/InferenceContext.tsx +++ b/packages/app/src/components/inference/InferenceContext.tsx @@ -159,24 +159,33 @@ export function InferenceProvider({ () => (getUrlParam('i_scale') as 'auto' | 'linear' | 'log') || 'auto', ); - // ── Quick filters (vendor / agg-disagg / mtp-stp) ─────────────────────────── + // ── Quick filters (vendor / framework / agg-disagg / mtp-stp) ──────────────── // Coarse pre-filters applied to the point set. Empty = no constraint. - const [quickFilterVendors, setQuickFilterVendors] = useState(() => { - const v = getUrlParam('i_vendor'); - return v ? v.split(',').filter(Boolean) : []; - }); - const [quickFilterFrameworks, setQuickFilterFrameworks] = useState(() => { - const v = getUrlParam('i_fw'); - return v ? v.split(',').filter(Boolean) : []; - }); - const [quickFilterDisagg, setQuickFilterDisagg] = useState(() => { - const v = getUrlParam('i_disagg'); - return v ? (v.split(',').filter(Boolean) as DisaggMode[]) : []; - }); - const [quickFilterSpec, setQuickFilterSpec] = useState(() => { - const v = getUrlParam('i_spec'); - return v ? (v.split(',').filter(Boolean) as SpecMode[]) : []; - }); + // + // Initialized empty rather than from the URL so the first client render matches + // SSR (which has no query string). Reading the params in these initializers would + // desync the pills' aria-pressed/disabled between server and client; React does + // not patch hydration mismatches, so a shared link would leave the pills frozen + // inactive/disabled even while the chart filters. The URL selections are applied + // just below, after mount. + const [quickFilterVendors, setQuickFilterVendors] = useState([]); + const [quickFilterFrameworks, setQuickFilterFrameworks] = useState([]); + const [quickFilterDisagg, setQuickFilterDisagg] = useState([]); + const [quickFilterSpec, setQuickFilterSpec] = useState([]); + useEffect(() => { + const parse = (key: 'i_vendor' | 'i_fw' | 'i_disagg' | 'i_spec') => { + const v = getUrlParam(key); + return v ? v.split(',').filter(Boolean) : []; + }; + const vendors = parse('i_vendor'); + const frameworks = parse('i_fw'); + const disagg = parse('i_disagg') as DisaggMode[]; + const spec = parse('i_spec') as SpecMode[]; + if (vendors.length > 0) setQuickFilterVendors(vendors); + if (frameworks.length > 0) setQuickFilterFrameworks(frameworks); + if (disagg.length > 0) setQuickFilterDisagg(disagg); + if (spec.length > 0) setQuickFilterSpec(spec); + }, [getUrlParam]); const quickFilters = useMemo( () => ({ vendors: quickFilterVendors,