76 Commits (master)

Author SHA1 Message Date
oabrivard f8588a57a3 fix: skip 401 redirect for auth endpoints to prevent login interference
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>
2 months ago
oabrivard d081c47c7f fix: prevent 401 redirect loop on login and register pages
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>
2 months ago
oabrivard b08d65c53c fix: use fresh Turnstile token for resend on Login and Register pages
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>
2 months ago
oabrivard d2851c019e fix: add timeout to Turnstile polling loop to prevent infinite retries
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>
2 months ago
oabrivard c6aa1afdc5 refactor: decompose ThemeSourceList into SourceAddForm + SourceImport
ThemeSourceList: 477 → 222 lines (source list + preferred + delete)
SourceAddForm: 114 lines (title + URL form)
SourceImport: 186 lines (CSV import/export + bulk text import)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2 months ago
oabrivard 42ced9cfee fix: swap date and source URL positions in article cards 2 months ago
oabrivard 598211167d feat: show source URL next to date in synthesis article cards
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>
2 months ago
oabrivard 48cad8144b fix: show schedule panel before sources in theme settings
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2 months ago
oabrivard 0a0684e42e refactor: decompose ThemeManager into ThemeContentForm + ThemeSourceList sub-components
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>
2 months ago
oabrivard b60a55993c fix: P2 audit items — use API client for stop, replace raw buttons, remove deprecated doc refs
- 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>
2 months ago
oabrivard b124d73c2a fix: P1 audit items — CSV export theme filter, theme validation, ownership checks, history enums, i18n
- 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>
2 months ago
oabrivard d5d624b896 fix: P0 audit bugs — theme-scoped imports/preferred, creation flow, scheduler timeout, job cleanup
- 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>
2 months ago
oabrivard 3dea7bf286 fix: replace hardcoded French strings with i18n keys in SynthesisDetail
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>
3 months ago
oabrivard 381e11a5e4 refactor: delete dead Sources.tsx, move URL utils to utils/url.ts
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>
3 months ago
oabrivard c51a99051f fix: update failing frontend tests for multi-theme migration
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>
3 months ago
oabrivard f989f592a9 feat: add schedule UI — day picker, time, emails in theme management
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
oabrivard e43a4d2180 feat: add preferred sources — prioritized during synthesis generation
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>
3 months ago
oabrivard 6f3e6883c9 feat: add stop generation button — saves partial synthesis on cancel
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>
3 months ago
oabrivard d2f98dc66f feat: Phase 4 — theme badge on synthesis cards + sort by date/theme
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
oabrivard ed29e74b1b feat: Phase 3 — add theme dropdown to Generate page
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
oabrivard d9bfed7ff5 feat: Phase 2 — theme management page with content settings + per-theme sources
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3 months ago
oabrivard 196005a27b feat: multi-theme Phase 1 — settings split, sources/syntheses theme_id, pipeline theme-aware
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>
3 months ago
oabrivard 3fa0156f52 fix: default display level to 3 (half summary) 3 months ago
oabrivard 44606ca3eb feat: add per-article expand/collapse button when display is compact
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>
3 months ago
oabrivard 2650356c01 feat: add display density slider to synthesis detail view
4 levels: Compact (title+date only) → 2 lines → half summary → full.
Slider in a gray bar above sections. Card spacing tightens at level 1.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3 months ago
oabrivard 6b84c335d0 feat: improve synthesis list cards with time, all categories, and uniform height
- 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>
3 months ago
oabrivard 03f2660163 refactor: redesign settings page with clear section grouping
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>
3 months ago
oabrivard 70865a68b3 refactor: redesign settings page with clear section grouping and merged AI section
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>
3 months ago
oabrivard 102f6d3fe7 feat: add first/last page + page number input to article history pagination
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3 months ago
oabrivard e24236a069 feat: add max_links_per_source setting (default 8, was hardcoded 15)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
oabrivard 2c3c6008a3 fix: monotonic progress bar with 3 clean phases (sources, websearch, saving)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3 months ago
oabrivard d234fa9b24 feat: add is_article LLM check + remove use_llm_for_source_links option
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>
3 months ago
oabrivard 0f1b0306e4 feat: add source_extraction_window setting (default 3)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
oabrivard c5a56c8fb8 feat: save publication date in article history and show in synthesis
- 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>
3 months ago
oabrivard bf07b049f3 feat: add summary length slider to Settings page
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
oabrivard 0aa0541958 refactor: standardize createResource and use Button component
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>
3 months ago
oabrivard 150090a112 chore: align frontend DEFAULT_SETTINGS with backend defaults
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
oabrivard ee6d833d70 refactor: decompose Settings.tsx into sub-components
Extract three self-contained sections from Settings.tsx (1025 lines)
into dedicated components under frontend/src/components/settings/:
- SettingsBraveSearch: Brave API key lifecycle + use_brave_search toggle
- SettingsAdvanced: article_history_days, batch_size, use_llm_for_source_links, search_agent_behavior
- SettingsRateLimit: rate limit inputs + effective rate display + reset

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3 months ago
oabrivard 5a66443a94 fix: add onCleanup for setTimeout in frontend pages
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>
3 months ago
oabrivard 3da246bef6 fix: remove XSS via innerHTML and add setTimeout cleanup
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
oabrivard c5d23ecd10 feat: add Brave Search section to Settings page
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>
3 months ago
oabrivard f414ff0f58 feat: add use_brave_search setting
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>
3 months ago
oabrivard a5332f0996 feat: add article_url to LLM call logs for classify tracing
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>
3 months ago
oabrivard 4c6381b09a feat: add batch_size setting for Phase 1 parallelism
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>
3 months ago
oabrivard a5e6cf2ac0 docs: update algorithm docs and generation time estimate
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>
3 months ago
oabrivard fb765d6c8f feat: split model dropdowns — scraping vs websearch in frontend
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>
3 months ago
oabrivard 37bc849f92 feat: add clear history button with confirmation on ArticleHistory page
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3 months ago
oabrivard c4a4cd9987 feat: remove deprecated settings from frontend
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago
oabrivard f9023cff7e feat: LLM logs viewer page + log button on Home synthesis list
- 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>
3 months ago
oabrivard cbe1cd6507 feat: LLM logs types, API client, and i18n labels
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 months ago