The API client's 401 handler was intercepting responses from /auth/*
endpoints (login, register, me), throwing "Session expired" before the
actual response could reach the caller. This prevented the login form
from working — the AuthProvider's me() call returned 401, threw, and
the error propagated into the login flow.
Now the 401 redirect only triggers for non-auth API calls (where it
genuinely indicates an expired session). Auth endpoints handle their
own error responses normally.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The API client redirected to /login on any 401 response, including the
GET /auth/me call made by AuthProvider on the login page itself. This
caused an infinite hard-navigation reload loop.
Skip the redirect when already on /login or /register — the AuthContext
route guards handle unauthenticated routing for those pages.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Turnstile tokens are single-use. The resend flow was reusing the consumed
token from the initial submission, causing "timeout-or-duplicate" errors.
- Add Turnstile widget to the resend view so a fresh token is obtained
- Add resetSignal prop to Turnstile component to re-solve after each resend
- Clear token after each successful API call to prevent stale reuse
- Guard handleResend against null token
- Add test for resetSignal behavior
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When the Cloudflare Turnstile script fails to load (e.g., 503 from CDN),
the polling interval ran forever, causing the page to appear stuck in a
refresh loop. Now stops after 100 attempts (10s) and calls onError.
Also adds dedicated unit tests for the Turnstile component covering
immediate render, delayed load, timeout, and cleanup-during-polling.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Date aligned left, source URL aligned right. URL stripped of protocol
and truncated to 40 chars with "..." if too long. Full URL on hover.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract content settings card and sources card into dedicated components,
reducing ThemeManager from 938 to 233 lines while keeping theme list CRUD
and selector in the parent.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace raw fetch in handleStop with synthesesApi.stop()
- Add stop() method to synthesesApi
- Replace raw <button> elements in GenerateSynthesis with Button component (generate, retry, stop)
- Remove deprecated LLM link extraction schema reference from technical_specs.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- export_csv now accepts optional theme_id query param and filters accordingly
- Add UpdateThemeRequest::validate() with bounds checking; call it in the update handler
- Verify theme ownership in sources::create when theme_id is provided
- Update STATUS_OPTIONS (add filtered_too_old, filtered_not_article; remove filtered_duplicate) and SOURCE_TYPE_OPTIONS (add brave_search; remove overflow) in ArticleHistory
- Replace hardcoded French strings ('Confirmer', 'Erreur inconnue') with t() calls; add settings.apiKeys.unknownError key to fr.ts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Bulk/CSV import now passes theme_id through to DB
- Preferred source update scoped by theme_id (no cross-theme reset)
- Theme creation sends sensible defaults from frontend
- Scheduler wraps generation in 15-minute timeout
- Job store cleanup runs every 5 minutes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace "Lire la suite" and "Reduire" with t('synthesis.readMore')
and t('synthesis.collapse'). Adds both keys to fr.ts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
normalizeUrl and isValidUrl are now in ~/utils/url. ThemeManager
and sources-utils tests import from the new location. The /sources
route redirect to /themes is preserved in App.tsx.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Settings tests now reference fields that exist in the current UI
(max articles per source instead of removed theme field). Generate
tests now mock the themes API and use current pipeline step names.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Users can mark sources as preferred via star buttons on the theme page.
Preferred sources are processed first in the pipeline (ordered before
non-preferred in waves, shuffled separately then merged).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds Arc<AtomicBool> cancellation flag to JobStore/JobEntry. The pipeline
checks the flag before each wave and after each batch, then saves whatever
articles have been collected. A new POST /syntheses/generate/:job_id/stop
endpoint sets the flag. The frontend shows a red stop button during generation
and POSTs to the stop endpoint on click.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove content settings from settings table (moved to themes).
Add theme_id to sources and syntheses. Pipeline loads content
settings from the selected theme. Generate endpoint requires theme_id.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When the slider is not at full (level < 4), each truncated article
shows a "Lire la suite" button to expand its summary inline.
A "Reduire" button collapses it back. Expansion is per-card and
independent of the global slider.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add generation time below date in synthesis cards
- Show all categories with article count in parentheses
- Use flex-col layout for uniform card height
- Add sections_summary to SynthesisListItem API response
- Add formatTime utility
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 sections organized by purpose:
1. Contenu — theme, categories, summary length
2. Sources — per-source limits, Brave Search
3. Intelligence Artificielle — provider + API keys merged in one visual card
4. Performance — batch size, history, rate limits
5. Import/Export — collapsed by default
Sticky save button, smaller number inputs, integrated API key status
badge in the AI provider card.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reorganize settings into 5 logical sections (Content, Sources, AI,
Performance, Import/Export) with visual cards. Merge AI provider
selection with API key management into one cohesive section.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The LLM now determines if scraped content is a real article during
classify (zero extra cost). The separate LLM link extraction option
is removed — heuristic extraction is sufficient.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add published_date column to article_history table
- Add date field to NewsItem (serialized in synthesis JSONB)
- Pass LLM-extracted date through ArticleTrace to article history
- Display date below article title in SynthesisDetail page
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Convert LlmLogs from onMount+signals to createResource for idiomatic
reactive data fetching. Replace duplicated inline save/add button markup
in Settings and Sources with the shared Button component.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Prevents timers from firing after component unmount in Home (delete
confirmation timers), ArticleHistory (clear confirmation timer), and
SynthesisDetail (email success banner timer).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a dedicated Brave Search section after the Advanced Extraction section,
including inline API key management (save/test/delete) and a use_brave_search
toggle that auto-disables when the key is removed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add use_brave_search boolean field to all settings structs, DB layer,
SQL queries, frontend types, i18n labels, and test fixtures following
the same pattern as use_llm_for_source_links.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds an optional article_url column to llm_call_log so classify_summarize
entries are traceable back to their source article in the LLM Logs UI.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add a user-configurable batch_size setting (default 5, range 1-20)
that controls how many articles are processed in parallel during
Phase 1 scrape+classify. Previously hardcoded to 5.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update algorithm.md to reflect the rewritten per-article classify/summarize
pipeline (no batch classification, no rewrite pass). Update generation time
estimate from 1 minute to 10 minutes in frontend i18n and docs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the single `models` array in `ProviderConfig` and `AdminProvider`
with separate `models_scraping` / `models_websearch` lists. Rename
`ai_model_writing` → `ai_model_websearch` in `UserSettings` and all
references (Settings page, admin Providers page, E2E test, fixtures,
and unit tests). Update i18n label for the second dropdown to
"Modele d'IA (Recherche Web)".
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add LlmLogs page with collapsible prompts/response sections, call-type
colored badges, and duration display
- Wire /llm-logs/:jobId route in App.tsx (lazy-loaded)
- Expose job_id in backend SynthesisListItem and frontend SynthesisListItem
type; update test fixture accordingly
- Add log-icon link next to delete button on each Home synthesis card
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>