From 6571201adf74619da8b85aee98743c97c85af975 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Tue, 16 Jun 2026 18:07:41 +0100 Subject: [PATCH 1/4] Fix for task page search bar re-rendering bug --- .../app/components/primitives/SearchInput.tsx | 14 +++++--- .../route.tsx | 33 +++++++++---------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/apps/webapp/app/components/primitives/SearchInput.tsx b/apps/webapp/app/components/primitives/SearchInput.tsx index 6b24e015e56..fca228b3926 100644 --- a/apps/webapp/app/components/primitives/SearchInput.tsx +++ b/apps/webapp/app/components/primitives/SearchInput.tsx @@ -47,18 +47,22 @@ export function SearchInput({ const [text, setText] = useState(initialSearch); const [isFocused, setIsFocused] = useState(false); + // Compare against a ref, not `text`, so the effect stays off the keystroke path. + const lastSyncedRef = useRef(initialSearch); + useEffect(() => { if (isControlled) { - if (controlledValue !== undefined && controlledValue !== text) { + if (controlledValue !== undefined && controlledValue !== lastSyncedRef.current) { + lastSyncedRef.current = controlledValue; setText(controlledValue); } return; } const urlSearch = value(paramName) ?? ""; - if (urlSearch !== text && !isFocused) { - setText(urlSearch); - } - }, [isControlled, controlledValue, value, text, isFocused, paramName]); + if (urlSearch === lastSyncedRef.current) return; + lastSyncedRef.current = urlSearch; + if (!isFocused) setText(urlSearch); + }, [isControlled, controlledValue, value, isFocused, paramName]); const updateText = (next: string) => { setText(next); diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam._index/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam._index/route.tsx index 6a48f324024..6bb6062e6ac 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam._index/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam._index/route.tsx @@ -193,7 +193,7 @@ export default function Page() { }, [streamedEvents]); // eslint-disable-line react-hooks/exhaustive-deps const [showUsefulLinks, setShowUsefulLinks] = useState(usefulLinksPreference ?? true); - // Unmount the charts while the side panel animates; 25 SVGs in a reflowing table tanks perf. + // Hide (don't unmount) the charts during the panel animation; 25 reflowing SVGs tank the resize. const [isPanelAnimating, setIsPanelAnimating] = useState(false); const animatingTimerRef = useRef | null>(null); const usefulLinksPanelRef = useRef(null); @@ -436,24 +436,21 @@ function TaskRow({ - {/* Reserve the cell footprint while the chart unmounts during the panel animation. */}
- {!isPanelAnimating && ( -
- }> - }> - {(data) => { - const taskData = data[item.slug]; - return taskData && taskData.length > 0 ? ( - - ) : ( - - ); - }} - - -
- )} +
Date: Tue, 16 Jun 2026 18:16:00 +0100 Subject: [PATCH 2/4] fix(webapp): sync search input on blur after URL change during focus Co-Authored-By: Claude Opus 4.8 (1M context) --- apps/webapp/app/components/primitives/SearchInput.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/webapp/app/components/primitives/SearchInput.tsx b/apps/webapp/app/components/primitives/SearchInput.tsx index fca228b3926..2208164782f 100644 --- a/apps/webapp/app/components/primitives/SearchInput.tsx +++ b/apps/webapp/app/components/primitives/SearchInput.tsx @@ -60,8 +60,11 @@ export function SearchInput({ } const urlSearch = value(paramName) ?? ""; if (urlSearch === lastSyncedRef.current) return; - lastSyncedRef.current = urlSearch; - if (!isFocused) setText(urlSearch); + // Only mark synced once we actually apply it, so a URL change during focus still syncs on blur. + if (!isFocused) { + lastSyncedRef.current = urlSearch; + setText(urlSearch); + } }, [isControlled, controlledValue, value, isFocused, paramName]); const updateText = (next: string) => { From b6705517bfda97c9201fe34c6662a5702bb30be1 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Tue, 16 Jun 2026 18:37:07 +0100 Subject: [PATCH 3/4] chore(webapp): add server-changes file for search bar fix --- .server-changes/search-input-rerender.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .server-changes/search-input-rerender.md diff --git a/.server-changes/search-input-rerender.md b/.server-changes/search-input-rerender.md new file mode 100644 index 00000000000..aea6f07c98b --- /dev/null +++ b/.server-changes/search-input-rerender.md @@ -0,0 +1,6 @@ +--- +area: webapp +type: fix +--- + +Fix the task page search bar clearing or resetting while typing, caused by a re-render race between the input sync effect and the activity charts. From fb28ba59551e1000cbfe738833b90cb780e83aa7 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Tue, 16 Jun 2026 18:57:51 +0100 Subject: [PATCH 4/4] docs(webapp): document controlled-mode sync trade-off in SearchInput --- apps/webapp/app/components/primitives/SearchInput.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/webapp/app/components/primitives/SearchInput.tsx b/apps/webapp/app/components/primitives/SearchInput.tsx index 2208164782f..ef1fdb8a496 100644 --- a/apps/webapp/app/components/primitives/SearchInput.tsx +++ b/apps/webapp/app/components/primitives/SearchInput.tsx @@ -48,6 +48,8 @@ export function SearchInput({ const [isFocused, setIsFocused] = useState(false); // Compare against a ref, not `text`, so the effect stays off the keystroke path. + // Trade-off: controlled mode assumes the parent accepts onValueChange; it won't + // re-sync `text` if the parent rejects a change and holds `value` unchanged. const lastSyncedRef = useRef(initialSearch); useEffect(() => {