Files
Momento/memento-note/hooks/use-title-suggestions.ts
Antigravity afbb0dfc2d
All checks were successful
CI / Lint, Unit Tests & Build (push) Successful in 6m57s
CI / Deploy production (on server) (push) Successful in 24s
fix(ui): thème, textes et lisibilité restants de la revue
Les boutons suivent la couleur d’apparence, les libellés trop petits ou trop techniques sont clarifiés, et le catalogue des fournisseurs se met à jour tout seul.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-30 21:13:07 +00:00

103 lines
2.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useState, useEffect, useRef } from 'react'
import { useDebounce } from './use-debounce'
import { useAiConsent } from '@/components/legal/ai-consent-provider'
import { countPlainWords, MIN_WORDS_FOR_AUTO_TITLE } from '@/lib/text/plain-text'
export interface TitleSuggestion {
title: string
confidence: number
reasoning?: string
}
interface UseTitleSuggestionsProps {
content: string
enabled?: boolean
}
export function useTitleSuggestions({ content, enabled = true }: UseTitleSuggestionsProps) {
const { requestAiConsent, hasAiConsent } = useAiConsent()
const [suggestions, setSuggestions] = useState<TitleSuggestion[]>([])
const [isAnalyzing, setIsAnalyzing] = useState(false)
const [error, setError] = useState<string | null>(null)
const abortRef = useRef<AbortController | null>(null)
// Debounce content by 2s to avoid excessive API calls
const debouncedContent = useDebounce(content, 2000)
useEffect(() => {
if (!enabled || !debouncedContent) {
setSuggestions([])
return
}
// Compter le texte visible, pas les balises de léditeur
if (countPlainWords(debouncedContent) < MIN_WORDS_FOR_AUTO_TITLE) {
setSuggestions([])
return
}
// Cancel previous request
abortRef.current?.abort()
const controller = new AbortController()
abortRef.current = controller
const generateTitles = async () => {
if (!hasAiConsent) {
const consented = await requestAiConsent()
if (!consented) {
setSuggestions([])
return
}
}
setIsAnalyzing(true)
setError(null)
try {
const response = await fetch('/api/ai/title-suggestions', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ content: debouncedContent }),
signal: controller.signal,
})
if (controller.signal.aborted) return
if (!response.ok) {
setSuggestions([])
if (response.status !== 400) {
setError('Failed to generate title suggestions')
}
return
}
const data = await response.json()
setSuggestions(data.suggestions || [])
} catch (err: any) {
if (err.name === 'AbortError') return
console.error('Title suggestions error:', err)
setSuggestions([])
setError('Failed to generate title suggestions')
} finally {
if (!controller.signal.aborted) {
setIsAnalyzing(false)
}
}
}
generateTitles()
}, [debouncedContent, enabled, hasAiConsent, requestAiConsent])
// Cleanup on unmount
useEffect(() => {
return () => { abortRef.current?.abort(); };
}, []);
return {
suggestions,
isAnalyzing,
error,
clearSuggestions: () => setSuggestions([])
}
}