From 30055ab8708c57ad1399c4c9ed3ccd1ef27aae83 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Wed, 25 Mar 2026 08:05:50 +0000 Subject: [PATCH] vibes --- frontend/src/App.tsx | 1 + frontend/src/components/map/AiFilterInput.tsx | 53 ++++++++-- frontend/src/components/map/Filters.tsx | 4 +- .../src/components/map/LocationSearch.tsx | 4 +- frontend/src/components/map/Map.tsx | 96 +++++++++---------- frontend/src/components/map/MapLegend.tsx | 2 +- frontend/src/components/map/MapPage.tsx | 49 +++++++--- .../src/components/map/TravelTimeCard.tsx | 8 +- .../src/components/ui/DestinationDropdown.tsx | 4 +- frontend/src/hooks/useMapData.ts | 10 +- frontend/src/hooks/useTravelDestinations.ts | 2 + frontend/src/lib/feature-icons.tsx | 13 +++ frontend/src/lib/url-state.ts | 2 +- 13 files changed, 165 insertions(+), 83 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 1c2f0f4..5e6c5b3 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -24,6 +24,7 @@ import { useSavedProperties } from './hooks/useSavedProperties'; declare global { interface Window { __screenshot_ready?: boolean; + __map_idle?: boolean; } } diff --git a/frontend/src/components/map/AiFilterInput.tsx b/frontend/src/components/map/AiFilterInput.tsx index dac5d89..0850e26 100644 --- a/frontend/src/components/map/AiFilterInput.tsx +++ b/frontend/src/components/map/AiFilterInput.tsx @@ -1,6 +1,7 @@ import { memo, useState, useCallback, useEffect, useRef } from 'react'; import { SpinnerIcon } from '../ui/icons/SpinnerIcon'; import { SparklesIcon } from '../ui/icons/SparklesIcon'; +import { ChevronIcon } from '../ui/icons/ChevronIcon'; import type { AiFilterErrorType } from '../../hooks/useAiFilters'; const EXAMPLE_QUERIES = [ @@ -65,18 +66,45 @@ export default memo(function AiFilterInput({ const [expanded, setExpanded] = useState(false); const loadingMessage = useLoadingMessage(loading); const containerRef = useRef(null); + const textareaRef = useRef(null); + + const queryRef = useRef(query); + queryRef.current = query; useEffect(() => { if (!expanded || loading) return; const handler = (e: MouseEvent) => { if (containerRef.current && !containerRef.current.contains(e.target as Node)) { - setExpanded(false); + if (!queryRef.current.trim()) setExpanded(false); } }; document.addEventListener('mousedown', handler); return () => document.removeEventListener('mousedown', handler); }, [expanded, loading]); + const resizeTextarea = useCallback(() => { + const ta = textareaRef.current; + if (!ta) return; + ta.style.height = 'auto'; + ta.style.height = `${ta.scrollHeight}px`; + }, []); + + const handleKeyDown = useCallback( + (e: React.KeyboardEvent) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + const trimmed = query.trim(); + if (!trimmed || loading) return; + if (!isLoggedIn) { + onLoginRequired(); + return; + } + onSubmit(trimmed); + } + }, + [query, loading, isLoggedIn, onLoginRequired, onSubmit] + ); + const handleSubmit = useCallback( (e: React.FormEvent) => { e.preventDefault(); @@ -132,14 +160,27 @@ export default memo(function AiFilterInput({ describe what you're looking for + -
- +