From 25eade4273c651697055f57efb6f4a1e8e9fd458 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 5 Jun 2026 11:13:40 +0000 Subject: [PATCH 1/6] feat(ux): ensure Clear button is visible when results are present Modified MainContent.tsx to show the Clear button whenever results are displayed, even if the search input is empty. This prevents users from getting stuck with results they cannot easily clear after manually emptying the search field. The Fetch button remains gated by the presence of a query. Co-authored-by: d-oit <6849456+d-oit@users.noreply.github.com> --- web/app/components/MainContent.tsx | 20 +++++++++++--------- web/package-lock.json | 4 ++-- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/web/app/components/MainContent.tsx b/web/app/components/MainContent.tsx index e197504..48c5996 100644 --- a/web/app/components/MainContent.tsx +++ b/web/app/components/MainContent.tsx @@ -124,16 +124,18 @@ export default function MainContent({ )} - {query.trim() && ( + {(query.trim() || result) && (
- + {query.trim() && ( + + )} + do-web-doc-resolver +
+ + Help + + +); + +interface SearchSectionProps { + query: string; + setQuery: (query: string) => void; + handleSubmit: (e?: React.FormEvent) => void; + loading: boolean; + inputRef: React.RefObject; + error: string; + result: string; + onClear: () => void; + providerStatus: string | null; + isUrl: boolean; +} + +const SearchSection = ({ query, setQuery, handleSubmit, @@ -46,183 +77,229 @@ export default function MainContent({ inputRef, error, result, - setResult, - setError, + onClear, providerStatus, - setProviderStatus, + isUrl, +}: SearchSectionProps) => ( +
+
+ +
+ setQuery(e.target.value)} + onKeyDown={(e) => e.key === "Enter" && handleSubmit()} + placeholder="URL or search query..." + aria-invalid={Boolean(error)} + aria-errormessage={error ? "search-error" : undefined} + className="w-full bg-transparent text-[20px] sm:text-[24px] text-foreground placeholder:text-text-dim tracking-tight pr-10" + /> + {query && ( + + )} +
+ {(query.trim() || result) && ( +
+ {query.trim() && ( + + )} + +
+ )} +
+ {query.trim() && ( +
+ {isUrl ? "Resolving as URL" : "Searching"} +
+ )} + {providerStatus && ( +
+ {providerStatus} +
+ )} +
+); + +interface MetadataBarProps { + sourceProvider: string | null; + resolveTime: number | null; + charCount: number; + qualityScore: number | null; + viewRaw: boolean; + setViewRaw: (view: boolean) => void; + handleCopyResult: () => void; + copied: boolean; +} + +const MetadataBar = ({ sourceProvider, - setSourceProvider, resolveTime, - setResolveTime, + charCount, qualityScore, - setQualityScore, - parsedResults, - setParsedResults, viewRaw, setViewRaw, - helpfulIds, - toggleHelpful, handleCopyResult, - handleCardCopy, copied, - isUrl, -}: MainContentProps) { +}: MetadataBarProps) => ( +
+
+ + Source: {sourceProvider} + + {resolveTime && {resolveTime}ms} + {charCount.toLocaleString()} chars + {qualityScore !== null && ( + + Quality:{" "} + = 70 ? "text-accent" : qualityScore >= 40 ? "text-[#ffaa00]" : "text-error" + } + > + {qualityScore} + + + )} +
+
+ + + +
+
+); + +export const MainContent = (props: MainContentProps) => { + const { + mobileMenuOpen, + setMobileMenuOpen, + query, + setQuery, + handleSubmit, + loading, + inputRef, + error, + result, + setResult, + setError, + providerStatus, + setProviderStatus, + sourceProvider, + setSourceProvider, + resolveTime, + setResolveTime, + qualityScore, + setQualityScore, + parsedResults, + setParsedResults, + viewRaw, + setViewRaw, + helpfulIds, + toggleHelpful, + handleCopyResult, + handleCardCopy, + copied, + isUrl, + } = props; + + const handleClear = () => { + setQuery(""); + setResult(""); + setError(""); + setProviderStatus(null); + setResolveTime(null); + setSourceProvider(null); + setQualityScore(null); + setParsedResults([]); + setViewRaw(false); + inputRef.current?.focus(); + }; + const charCount = result.length; return (
- {/* Header */} -
-
- {/* Hamburger menu - mobile only */} - - do-web-doc-resolver -
- - Help - -
+ - {/* Input */} -
-
- -
- setQuery(e.target.value)} - onKeyDown={(e) => e.key === "Enter" && handleSubmit()} - placeholder="URL or search query..." - aria-invalid={!!error} - aria-errormessage={error ? "search-error" : undefined} - className="w-full bg-transparent text-[20px] sm:text-[24px] text-foreground placeholder:text-text-dim tracking-tight pr-10" - /> - {query && ( - - )} -
- {(query.trim() || result) && ( -
- {query.trim() && ( - - )} - -
- )} -
- {query.trim() && ( -
- {isUrl ? "Resolving as URL" : "Searching"} -
- )} - {providerStatus && ( -
- {providerStatus} -
- )} -
+ - {/* Error */} {error && ( )} - {/* Output */}
{result ? ( <> - {/* Metadata bar */} -
-
- - Source: {sourceProvider} - - {resolveTime && {resolveTime}ms} - {charCount.toLocaleString()} chars - {qualityScore !== null && ( - - Quality: = 70 ? "text-accent" : qualityScore >= 40 ? "text-[#ffaa00]" : "text-error"}>{qualityScore} - - )} -
-
- - - -
-
+ {viewRaw || parsedResults.length === 0 ? (