import { X } from 'lucide-react'; import { useMemo } from 'react'; import { useTranslation } from '../../../i18n'; import { chatOf, useAssistantStore } from '../../../store/assistantStore'; import { variantPaintCss } from '../../Tours/planner/legVariantPaint'; import { VariantMark } from '../../Tours/planner/mapCardParts'; import { type AssistantView, AssistantViewContext, useAssistantView } from '../assistantViewContext'; import { TourGrade, TourRouteFigures } from '../cards/tour/TourRouteHead'; import { profileSamples, WAY_KEYS } from '../cards/tour/tourRouteModel'; import TourRouteProfile from '../cards/tour/TourRouteProfile'; import { routeName } from '../cards/tour/tourRouteWords'; import { useRouteFocus } from '../cards/tour/useTourRouteCard'; import { moveRoutePeek } from './routePeekControl'; import { type PeekRoute, peekRoute } from './routePeekModel'; /** The peek's body, for the route found: its ways, its name and figures, its profile and the way to close it. */ function PeekBody({ route }: { route: PeekRoute }) { const { t } = useTranslation(); const { tripId } = useAssistantView(); const { entry, ways, blockKey } = route; const { card, toolCallId } = entry; const pick = useAssistantStore((s) => s.pick); const shown = useRouteFocus(toolCallId); const samples = useMemo(() => profileSamples(card), [card]); const index = ways.findIndex((way) => way.toolCallId === toolCallId); const color = variantPaintCss(index >= 0 ? (WAY_KEYS[index] ?? 'a') : 'current'); const name = routeName(card, t); const waysLabel = t('assistant.card.tour.ways', { count: ways.length }); return (

{name}

{ways.length > 0 && (
{ways.map((way, position) => { const key = WAY_KEYS[position] ?? 'a'; const letter = key.toUpperCase(); const chosen = way.toolCallId === toolCallId; return ( ); })}
)}
); } /** Finds the route the peek is open on in this trip's chat; nothing while it is closed or the route is gone. */ function PeekFinder() { const { tripId } = useAssistantView(); const toolCallId = useAssistantStore((s) => (s.routePeek?.tripId === tripId ? s.routePeek.toolCallId : null)); const messages = useAssistantStore((s) => chatOf(s, tripId).messages); const route = useMemo(() => (toolCallId ? peekRoute(messages, toolCallId) : null), [messages, toolCallId]); return route ? : null; } interface RoutePeekProps { tripId: number; onOpen: NonNullable; onFocusDay: NonNullable; onRevealMap: NonNullable; } /** * The phone's peek at a route over the map, while the chat stands aside: * opened by "On map" on a route card or a tap on a route line. It shows the * route as its card does in brief (name, grade, figures) and its height * profile with the readout under it, a finger scrubbing along it while the * map's pin follows; the ways of an answer as their marks, 44 px targets, a * tap choosing one as the card's choice does; and a close button. The map * stays usable around it, and the chat's pill below brings the chat back. */ export default function RoutePeek({ tripId, onOpen, onFocusDay, onRevealMap }: RoutePeekProps) { const view = useMemo( () => ({ tripId, variant: 'fullscreen', density: 'compact', onOpen, onFocusDay, onRevealMap }), [tripId, onOpen, onFocusDay, onRevealMap] ); return ( ); }