import { useState, useEffect, useRef, useCallback } from 'react' import { TrendingUp, Clock, Sparkles, MessageSquarePlus, Loader2, Plus, X, Send, ChevronDown, ChevronUp } from 'lucide-react' import { summariesApi, type Summary } from '@/api/summaries' import { reportsApi } from '@/api/reports' import { assetsApi, type Asset } from '@/api/assets' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Markdown } from '@/components/ui/markdown' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' import { Spinner } from '@/components/ui/spinner' import { useAuth } from '@/lib/auth' // ── Text-selection floating button ───────────────────────────────────────── function useTextSelection(containerRef: React.RefObject) { const [selection, setSelection] = useState<{ text: string; x: number; y: number } | null>(null) useEffect(() => { function readSelection() { const sel = window.getSelection() const text = sel?.toString().trim() if (!text || !containerRef.current) { setSelection(null); return } const range = sel!.getRangeAt(0) const rect = range.getBoundingClientRect() const containerRect = containerRef.current.getBoundingClientRect() setSelection({ text, x: rect.left - containerRect.left + rect.width / 2, y: rect.top - containerRect.top - 8, }) } function onMouseUp() { readSelection() } // On mobile, selectionchange fires after the user lifts their finger function onSelectionChange() { const sel = window.getSelection() if (!sel?.toString().trim()) setSelection(null) else readSelection() } function onPointerDown(e: PointerEvent) { if (!(e.target as Element).closest('[data-context-action]')) setSelection(null) } document.addEventListener('mouseup', onMouseUp) document.addEventListener('selectionchange', onSelectionChange) document.addEventListener('pointerdown', onPointerDown) return () => { document.removeEventListener('mouseup', onMouseUp) document.removeEventListener('selectionchange', onSelectionChange) document.removeEventListener('pointerdown', onPointerDown) } }, [containerRef]) return selection } // ── Context panel (extraits + question) ──────────────────────────────────── function ContextPanel({ excerpts, onRemove, onClear, onSubmit, }: { excerpts: string[] onRemove: (i: number) => void onClear: () => void onSubmit: (question: string) => Promise }) { const [question, setQuestion] = useState('') const [submitting, setSubmitting] = useState(false) const [submitted, setSubmitted] = useState(false) const [showExcerpts, setShowExcerpts] = useState(false) async function submit() { if (!question.trim() || submitting) return setSubmitting(true) try { await onSubmit(question) setSubmitted(true) setQuestion('') setTimeout(() => setSubmitted(false), 2000) } finally { setSubmitting(false) } } return (
{/* Header */}
{/* Extraits — toujours visible sur desktop, togglable sur mobile */}
{excerpts.map((e, i) => (
« {e} »
))}
{/* Question */}