import { AuiIf, ThreadPrimitive } from '@assistant-ui/react'; import { ArrowDown, FileUp, MapPin } from 'lucide-react'; import { useMemo } from 'react'; import { useTranslation } from '../../i18n'; import { chatOf, useAssistantStore } from '../../store/assistantStore'; import { useTripStore } from '../../store/tripStore'; import AssistantComposer from './AssistantComposer'; import AssistantError from './AssistantError'; import AssistantMessage from './AssistantMessage'; import AssistantPrivacyBanner from './AssistantPrivacyBanner'; import AssistantWelcome, { AssistantSuggestions } from './AssistantWelcome'; import { useAssistantView } from './assistantViewContext'; import { useAssistantAttach } from './useAssistantAttach'; import { useFollowSelectedDay } from './useAssistantFocus'; import { joinDropZones, useAssistantPlaceDrop } from './useAssistantPlaceDrop'; import { useAssistantSuggestions } from './useAssistantSuggestions'; /** * The chat itself: the messages, and under them the line that says what went * wrong, the privacy note and the composer, held at the bottom while the * messages scroll. A new question moves up to the top of the view, so its * answer reads from its first line instead of chasing the newest one. A * booking file or a place of the trip can be dropped anywhere on it. */ export default function AssistantThread({ tripTitle }: { tripTitle: string }) { const { t } = useTranslation(); const { tripId, variant } = useAssistantView(); const messages = useAssistantStore((s) => chatOf(s, tripId).messages); const isRunning = useAssistantStore((s) => chatOf(s, tripId).isRunning); const byId = useMemo(() => new Map(messages.map((m) => [m.id, m])), [messages]); const { suggestions, canEdit } = useAssistantSuggestions(tripId); useFollowSelectedDay(tripId); const phone = variant === 'fullscreen'; const attach = useAssistantAttach(tripId); const placeDrop = useAssistantPlaceDrop(tripId); const DropIcon = placeDrop.dragging ? MapPin : FileUp; // `@` reaches only the trip the planner shows; for another one the hint would promise nothing. const mentionable = useTripStore((s) => s.trip?.id === tripId); return ( {(attach.dragging || placeDrop.dragging) && (
)} s.thread.isEmpty}> {mentionable && (

{t('assistant.mention.hint')}

)}
{({ message }) => { const stored = byId.get(message.id); return stored ? : null; }}
{/* Outside the scroller, so it needs no backdrop of its own: on the glass window a second surface behind the composer showed as a box. */}
{phone && ( s.thread.isEmpty}> )}
); }