Commit Graph

57 Commits

Author SHA1 Message Date
Giancarmine Salucci
a389b0db15 fix(detection+tandoor): handle stepless Instagram recipes
Some checks failed
Build & Push Docker Image / test-and-build (push) Failing after 33s
Many Instagram recipe posts list ingredients without preparation steps,
directing users to the 'link in bio' for the full recipe.

- Detection prompt: removed step requirement entirely — title + 2
  ingredients is sufficient to detect a recipe
- tandoor.ts: when steps array is null/empty, create a single
  placeholder step so all ingredients are preserved in Tandoor

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-13 02:45:21 +02:00
Giancarmine Salucci
d09bf80088 fix(parser): relax detection prompt — quantities not required for social media recipes
Some checks failed
Build & Push Docker Image / test-and-build (push) Failing after 34s
Instagram recipes frequently list ingredients without quantities.
The old prompt required 'at least 3 ingredients WITH quantities' which
caused valid Italian social-media recipe posts to be rejected.

New criteria: dish name + 3 ingredients (any form) + 1 preparation step.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-13 02:37:59 +02:00
Giancarmine Salucci
226b2e7f15 fix(extraction): always use DOM extraction, never trust GraphQL caption
Some checks failed
Build & Push Docker Image / test-and-build (push) Failing after 33s
Instagram's GraphQL API silently truncates captions WITHOUT '….' markers.
Both DWWxiymssxE (393 chars full, 327 from API) and DXT73izCBoH
(744+ chars full, cut mid-sentence) were affected.

Remove the GraphQL-interception shortcut entirely. Always use DOM
extraction (HTML Section) which clicks '… more' to get the complete text.

The intercepted GraphQL caption is kept only as emergency fallback if
all DOM strategies fail.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-13 02:24:40 +02:00
Giancarmine Salucci
73e10730dc fix(extraction): don't use truncated GraphQL caption — fall through to DOM
Some checks failed
Build & Push Docker Image / test-and-build (push) Failing after 35s
If the GraphQL-intercepted caption ends with '….' (Instagram's truncation
marker), skip it and fall through to HTML Section extraction which clicks
the '… more' button in the DOM to get the complete, untruncated caption.

Previously the 327-char truncated caption for DWWxiymssxE was returned
immediately, causing the LLM to say 'no recipe' even though the full
description had all ingredients and steps.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-13 01:52:02 +02:00
Giancarmine Salucci
c9f5300272 feat: use Playwright for caption, yt-dlp for thumbnail only
Some checks failed
Build & Push Docker Image / test-and-build (push) Failing after 33s
Always extract the full caption via Playwright (browser sees the
untruncated text). yt-dlp runs in parallel only to get the thumbnail
CDN URL quickly; its result for the description is discarded.

This eliminates the truncation problem at the source without needing
a fallback heuristic.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-13 01:31:33 +02:00
Giancarmine Salucci
958353d15a feat: Playwright fallback for truncated Instagram captions
All checks were successful
Build & Push Docker Image / test-and-build (push) Successful in 1m1s
When yt-dlp returns a caption ending with the truncation marker '….'
(GraphQL API caps the text), automatically retry with the Playwright
extractor, which intercepts the full caption from live GraphQL network
traffic.

Falls back gracefully to the partial yt-dlp caption if Playwright fails.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-13 00:17:36 +02:00
Giancarmine Salucci
10c4f78ace Revert "feat: auto Playwright fallback when yt-dlp caption is truncated"
All checks were successful
Build & Push Docker Image / test-and-build (push) Successful in 1m3s
This reverts commit 8c25bce400.
2026-05-12 23:49:34 +02:00
Giancarmine Salucci
8c25bce400 feat: auto Playwright fallback when yt-dlp caption is truncated
All checks were successful
Build & Push Docker Image / test-and-build (push) Successful in 1m2s
Instagram truncates long captions server-side (ends with '…').
When yt-dlp returns a truncated caption, automatically fall back to
the Playwright extractor which runs JS in a real browser and can
click the 'more' button to expand the full caption.

Falls back gracefully: if Playwright fails, the truncated text is
still used rather than failing the whole extraction.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-12 23:46:24 +02:00
Giancarmine Salucci
22280d5536 feat(pwa): dynamic theme-color meta tags + transparent/dark mode icons
All checks were successful
Build & Push Docker Image / test-and-build (push) Successful in 1m3s
- +layout.svelte: replace Svelte logo favicon with actual InstaChef icons;
  add two <meta name="theme-color"> tags with media queries so the browser
  chrome (mobile top bar) matches --bg for light (#FFF8F5) and dark (#110510);
  add <meta name="color-scheme" content="dark light">
- manifest.json: split 'any maskable' into separate 'any' and 'maskable' entries;
  maskable uses icon-512-maskable.png (icon with 10% safe-zone padding on gradient bg)
- New icons:
  - icon-256/512.png → replaced with transparent-background versions
  - icon-256/512-transparent.png → white bg removed via flood-fill BFS
  - icon-256/512-dark.png → transparent icon on brand gradient (#833AB4→#E1306C)
  - icon-512-maskable.png → 80% icon centered on gradient (PWA maskable safe zone)
  - favicon-32.png → 32x32 transparent icon for browser tab
  - favicon.png (192×192) → updated to transparent InstaChef icon

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-12 23:33:57 +02:00
Giancarmine Salucci
9e14613746 fix(auth): always regenerate cookies.txt from auth.json, don't skip if yt-dlp overwrote it
All checks were successful
Build & Push Docker Image / test-and-build (push) Successful in 1m2s
Previously cookies.txt was only regenerated when auth.json was newer. But yt-dlp
overwrites cookies.txt during extraction with its own header ('generated by yt-dlp')
and potentially fewer/different cookies, losing the sessionid from auth.json.

Fix: remove mtime comparison — always regenerate cookies.txt from auth.json on each
extraction call. This ensures the full session cookie set is always present.
Also remove the now-unused statSync import.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-12 23:19:55 +02:00
Giancarmine Salucci
561c2843b1 feat(ui): add delete button to RecipeSheet + fix NaNd ago + full QueueItem in POST response
All checks were successful
Build & Push Docker Image / test-and-build (push) Successful in 1m1s
- RecipeSheet: add onDelete prop and 'Remove from queue' button at bottom of sheet
- +page.svelte: wire onDelete -> removeItem in RecipeSheet
- POST /api/queue: return full QueueItem (with createdAt, phases) instead of stripped subset
- TimelineRow: defensive relTime() handles undefined/NaN, uses createdAt ?? enqueuedAt

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-12 23:05:44 +02:00
Giancarmine Salucci
1f3bfe2119 fix(ui): fix NaNd ago - return full QueueItem from POST /api/queue + defensive relTime
All checks were successful
Build & Push Docker Image / test-and-build (push) Successful in 1m3s
- POST /api/queue now returns the full QueueItem (with createdAt, phases, etc.)
  instead of a stripped {id,url,status,enqueuedAt} subset
- TimelineRow.relTime() now handles undefined/NaN gracefully, falls back to 'just now'
- TimelineRow timestamp uses item.createdAt ?? item.enqueuedAt as fallback

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-12 23:00:52 +02:00
Giancarmine Salucci
8d979a9305 fix(ui): destructure {item} from POST /api/queue response
All checks were successful
Build & Push Docker Image / test-and-build (push) Successful in 1m1s
submitUrl() was using the full {duplicate, item} response object
as the queue item, causing 'Cannot read properties of undefined
(reading length)' crash when rendering phases in RecipeSheet/
TimelineRow.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-12 22:50:53 +02:00
Giancarmine Salucci
040ae17c12 fix(ui): add ic-btn-reset CSS + auto-convert auth.json to cookies.txt
All checks were successful
Build & Push Docker Image / test-and-build (push) Successful in 1m3s
- layout.css: add button.ic-btn-reset rule so all icon buttons
  (bell, back, close, retry, etc.) get proper background:none reset
  instead of browser-default white/grey appearance in dark mode
- instagram-extractor.ts: auto-convert secrets/auth.json
  (Playwright storage format) to Netscape cookies.txt at runtime
  whenever auth.json is newer; ensures sessionid and all Instagram
  session cookies are passed to yt-dlp, fixing empty media response

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-12 22:29:12 +02:00
Giancarmine Salucci
bd00595ded fix(test): mock $env/dynamic/private in llm-logging spec
Some checks failed
Build & Push Docker Image / test-and-build (push) Failing after 37s
Tests passed locally because .env provided OPENAI_BASE_URL and
OPENAI_API_KEY. In the Docker build stage there is no .env, so
createLLM() threw 'OPENAI_BASE_URL environment variable is not set'
before the mocked OpenAI client ever ran, causing 3 test failures.

Add vi.mock('$env/dynamic/private', ...) with stub values so the
tests are self-contained and environment-independent.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-12 22:13:20 +02:00
Giancarmine Salucci
d36629d5f0 fix(ci): run only server tests in Docker tester stage
Some checks failed
Build & Push Docker Image / test-and-build (push) Failing after 38s
Playwright Chromium is not available in node:24-alpine, causing the
vitest 'client' project (browser tests) to fail with an unhandled
browserType.launch error and exit code 1.

- Dockerfile: switch tester stage command to
  'npm run test:unit -- --run --project=server'
  so only Node.js unit tests run during Docker builds
- page.svelte.spec.ts: update stale 'renders h1' assertion to match
  the new InstaChef design (no h1; check for 'InstaChef' logo text)

Browser component tests still run locally when Playwright/Chromium
is available.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-12 22:09:57 +02:00
Giancarmine Salucci
573cf49ac5 feat(ui): implement InstaChef design system
Some checks failed
Build & Push Docker Image / test-and-build (push) Failing after 38s
- Replace Tailwind with IC CSS design tokens (purple/pink/orange brand gradient,
  Lilita One / DM Sans / JetBrains Mono fonts, light+dark theme via data-theme)
- Add all SVG icon components (ic/Bell, BellOff, Check, Chevron, Clipboard,
  Close, Download, External, Filter, Link, Plus, Retry, Search, Settings,
  Share, Spark, Trash, PhasePrepping, PhaseSimmering, PhasePlating)
- Add shared primitives: Chip, RecipeThumb (deterministic gradient swatch),
  CookingPot (animated SVG), PhaseTrack, SectionHead
- Add TopBar with LIVE indicator and notification bell
- Add CookingHero: animated hero card for in-progress items
- Add TimelineRow: queue list row with status badges
- Add EmptyState: gradient hero + dismissible How it works card
- Add RecipeSheet: bottom-sheet detail overlay with phase progress
- Add AddUrlScreen: full-page URL input with clipboard paste
- Add NotificationsScreen: push toggle + SSE status
- Rewrite +page.svelte: screen router (home/addurl/notifs) + RecipeSheet;
  preserves all SSE, retry, remove, filter, auto-subscribe logic
- Rewrite share/+page.svelte: uses AddUrlScreen shell, preserves Share Target
  logic and auto-process on URL param
- Rewrite InstallPrompt.svelte: InstallSheet bottom-sheet design, all PWA logic intact
- Update manifest.json theme_color to #FFF8F5
- 282 unit tests passing (unchanged)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-12 22:02:47 +02:00
Giancarmine Salucci
0b9f598c7d fix(parser): handle thinking models in recipe detection
Some checks failed
Build & Push Docker Image / test-and-build (push) Failing after 38s
Increase max_tokens from 10 to 1024 for detection so thinking
models have room to reason. Also fall back to reasoning_content
if content is empty, since some local models (e.g. Gemma 4
thinking variants) put their answer there.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-12 21:11:50 +02:00
Giancarmine Salucci
5b5bb947ef feat: replace Playwright extractor with yt-dlp subprocess
- Add instagram-extractor.ts: yt-dlp subprocess backend for Instagram
  caption extraction. No in-process browser state, maintained against
  Instagram frontend churn, supports cookies.txt for auth-walled reels.
- Add feature flag EXTRACTOR_BACKEND (ytdlp|playwright) in QueueProcessor
  so the old Playwright path remains available as fallback.
- Add 9 unit tests and 2 live-network integration tests for the new extractor.
- Dockerfile: install yt-dlp via pip3 alongside existing Chromium deps.
- docker-compose: expose EXTRACTOR_BACKEND env var (default: ytdlp).

Also in this commit:
- LLM: configurable per-request timeout via LLM_REQUEST_TIMEOUT_MS (default 120s);
  set maxRetries=0 to surface errors immediately; llama-swap /running health probe.
- QueueProcessor: thread progress callback through parser phase.
- LlmHealthIndicator: surface llama-swap loaded-model name.
- Logging: improve error serialization in queue-processor tests.
- .env.example: document llama-swap endpoint and model options.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-12 20:46:31 +02:00
Giancarmine Salucci
6849a1fb26 feat(RECIPE-0009): complete iteration 2 — ARIA-compliant footer icon contrast
Updated footer status bar icon colors from Tailwind 400-level to 600-level
variants to meet WCAG 2.1 SC 1.4.11 (3:1 minimum contrast ratio).

Changes:
- Notification icons: text-gray-400 → text-gray-600 (4.54:1 contrast)
- Status dots: bg-{green,yellow,red}-400 → bg-{green,yellow,red}-600
  (3.94:1, 4.02:1, 4.69:1 contrast respectively)

All footer icon states now exceed WCAG AA requirements by 31%+.
Build: PASSED | Tests: 278/278 PASSED
2026-02-19 10:06:57 +01:00
Giancarmine Salucci
08602073ac feat(RECIPE-0009): complete iteration 1 — footer status bar, icon-only buttons 2026-02-18 10:35:51 +01:00
Giancarmine Salucci
dfca35bde2 feat(RECIPE-0009): complete iteration 0 — deduplication, notifications, UI improvements 2026-02-18 06:00:48 +01:00
Giancarmine Salucci
49bccf8f15 simplify 2026-02-18 01:21:44 +01:00
Giancarmine Salucci
54321fd7c9 fix tests 2026-02-18 01:11:03 +01:00
Giancarmine Salucci
bf3e5c679f fix(RECIPE-0008): complete iteration 1 — resolve all TypeScript strict mode errors 2026-02-18 00:56:12 +01:00
Giancarmine Salucci
ea535bd9dd fix instagram extraction 2026-02-17 19:52:25 +01:00
Giancarmine Salucci
56d3aec3e2 fix(RECIPE-0006): complete iteration 1 - unit tests for Instagram caption extraction
- Exported cleanText() and extractFromDOM() for unit testing
- Fixed metadata prefix regex to handle optional quotes
- Created comprehensive unit tests with mocked Playwright Page (15 tests, 12ms)
- All 275 tests passing
2026-02-17 11:03:33 +01:00
Giancarmine Salucci
b304f5266a fix(RECIPE-0006): complete iteration 0 — fix Instagram recipe extraction 2026-02-17 10:14:52 +01:00
Giancarmine Salucci
b0b5c3579b fix(RECIPE-0005): complete iteration 0 — Playwright Alpine fix and Docker LMStudio setup 2026-02-17 04:19:55 +01:00
Giancarmine Salucci
67ab3c02d7 chore(RECIPE-0004): complete iteration 1 — fix TypeScript Timer type errors
- Fixed NodeJS.Timer → NodeJS.Timeout in scheduler.ts line 13
- Fixed NodeJS.Timer[] → NodeJS.Timeout[] in fixtures.ts line 151
- Resolves TypeScript compile errors from iteration 0 review
- All 260 tests passing, build succeeds with no errors
2026-02-17 03:08:21 +01:00
Giancarmine Salucci
7479d73662 fix(RECIPE-0003): complete iteration 3 - fix health check endpoint
- Fixed health endpoint to use getAll() instead of getAllItems()
- Removed call to non-existent getStats() method
- Added local stats computation with total count
- Health endpoint now returns 200 OK (was returning 500)
- Docker healthcheck now passes successfully
- No more TypeError in Docker logs

Resolves health check failure that was blocking Docker monitoring.
2026-02-16 22:16:05 +01:00
Giancarmine Salucci
8aafbb9d88 feat(RECIPE-0003): complete iteration 2 - fix Docker deployment
- Updated Dockerfile base image: node:22-alpine → node:24-alpine
- Regenerated package-lock.json to sync with package.json Tailwind v4
- Docker build now completes successfully (npm ci no longer fails)
- Docker compose with .env.example runs without errors
- Application verified accessible and functional in Docker
- Instagram extraction pipeline tested successfully

Resolves package-lock.json sync issue that blocked iteration 1.
2026-02-16 18:26:59 +01:00
Giancarmine Salucci
d55bcf9ae3 feat(RECIPE-0003): complete iteration 0 — update icon and add docker deployment 2026-02-16 15:56:23 +01:00
Giancarmine Salucci
0ab89a125f fix(RECIPE-0001): complete iteration 0 — automatic model loading and error display fix 2026-02-15 03:18:12 +01:00
Giancarmine Salucci
e49dbfae41 feat: fix push notifications and enhance PWA experience
- Fix InvalidCharacterError in push notifications with proper VAPID key validation
- Add attractive PWA install prompt component with cross-browser support
- Make notification settings always visible regardless of queue status
- Implement PWA install manager with user engagement detection
- Use SvelteKit navigation APIs instead of browser history API
- Add comprehensive error handling and logging
- Include cross-browser compatibility and responsive design
- Add development tooling improvements

Fixes push notification bugs and significantly improves PWA user experience
with modern, accessible interface components and proper error handling.
2025-12-22 15:18:03 +01:00
Giancarmine Salucci
051f76f64c chore: finalize migration cleanup
- Remove dev-dist/registerSW.js (no longer needed without vite-pwa plugin)
- Fix import order in layout.svelte
- Complete migration to native SvelteKit PWA
2025-12-22 05:49:05 +01:00
Giancarmine Salucci
b1c84fb837 feat(pwa): migrate service worker to SvelteKit native
Story 3: Migrate Service Worker to SvelteKit Native
- Replace workbox imports with SvelteKit $service-worker module
- Use build, files, version arrays for manual cache management
- Implement manual asset caching and cache cleanup
- Replace NavigationRoute with manual fetch handling
- Preserve all push notification event handlers exactly
- Preserve background sync and message handling functionality
- Service worker builds successfully as service-worker.mjs

SvelteKit native implementation ready - now need to enable registration

Refs: docs/plans/MigrateToNativeSvelteKitPWA.md
2025-12-22 05:29:37 +01:00
Giancarmine Salucci
e8bcc09f29 feat(pwa): create native PWA manifest.json
Story 1: Create Native PWA Manifest
- Extract manifest configuration from vite.config.ts to static/manifest.json
- Preserve exact configuration including share_target for Instagram URLs
- Update app.html to reference new manifest.json location
- Validated JSON syntax successfully

All PWA properties maintained:
- Share target: /share route for external app sharing
- Icons: favicon.png in 192x192 and 512x512 sizes
- Display: standalone mode for PWA installation
- Theme colors: white background and theme colors

Refs: docs/plans/MigrateToNativeSvelteKitPWA.md
2025-12-22 05:27:17 +01:00
Giancarmine Salucci
93aa25a31c fix: resolve critical app functionality issues
Complete implementation of fixes for queue processing, SSE connection display, service worker installation, and failing tests.

Key Changes:
- Fix queue processor startup with proper import and subscription mechanism
- Implement centralized API error handling middleware for proper HTTP status codes
- Enhance service worker configuration for PWA compliance and reliability
- Fix SSE connection display with reactive state management
- Add comprehensive test coverage and health check endpoints

Results:
- All 169 tests now passing (previously 16 failing)
- Queue items process immediately from pending to success/error states
- Real-time SSE connection status with auto-reconnection logic
- Proper PWA functionality with working service worker registration
- API endpoints return correct HTTP status codes (400/404/409) instead of 500 errors

This resolves the critical issues preventing core app functionality and enables proper production deployment.
2025-12-22 04:27:59 +01:00
Giancarmine Salucci
6b022d8348 feat(validation): relax Instagram URL validation to support all content types
- Create validateInstagramUrl utility using URL constructor
- Replace regex-based validation with hostname and protocol checks
- Support posts, reels, IGTV, and URLs with query parameters
- Add comprehensive unit tests (22 tests, all passing)
- Add integration tests for new URL formats
- Update API documentation with supported URL formats

Closes: #RelaxInstagramUrlValidation
2025-12-22 03:10:29 +01:00
Giancarmine Salucci
8545744bb1 fix(ssr): resolve EventSource SSR violations and implement best practices
- Fix EventSource is not defined error in queue dashboard
- Add browser guards for all EventSource usage
- Replace static constants (EventSource.OPEN/CLOSED) with numeric values
- Fix setInterval SSR violation in LLM health indicator
- Replace $effect anti-pattern with onMount in share page
- Add comprehensive SvelteKit SSR best practices documentation
- Add SSR audit and testing verification

All changes follow SvelteKit best practices and are verified against
official documentation. Production build succeeds with no SSR errors.

Closes: FixEventSourceSSR
See: docs/outcomes/FixEventSourceSSR.md
2025-12-22 03:00:29 +01:00
Giancarmine Salucci
767b8a1b37 feat(extraction): enhance thumbnail URL validation with strict HTTP 200 check
- Implement strict HTTP 200 validation (reject all other status codes)
- Add content-type validation (must be image/*)
- Add 10-second timeout protection with AbortController
- Thread progressCallback through all fetchImageAsBase64 calls
- Add detailed logging for each validation failure scenario
- Report validation failures via SSE progress callbacks

Unit tests:
- Add comprehensive test coverage for all validation scenarios
- Test HTTP status codes (200, 404, 403, 500, etc.)
- Test content-type validation (image/* vs text/html, etc.)
- Test timeout behavior with AbortController
- Test error handling (network errors, DNS, SSL, etc.)
- Test progress callback reporting

Integration tests:
- Add tests for complete extraction flow with URL failures
- Test fallback chain behavior (meta tags → poster → Instagram data → screenshot)
- Test real-world scenarios (redirects, query params, different post types)

Documentation:
- Enhanced JSDoc with validation criteria
- Added examples showing fallback behavior
- Documented all failure scenarios and their handling

All tests passing 
2025-12-21 05:33:48 +01:00
Giancarmine Salucci
5fe0a8a96e fix(tandoor): convert Buffer to Uint8Array for Blob compatibility
TypeScript compiler error fixed: Buffer is not assignable to BlobPart.
Convert Buffer to Uint8Array before creating Blob.
2025-12-21 05:19:45 +01:00
Giancarmine Salucci
cc7b8032cb fix(tandoor): use File constructor for proper multipart uploads
- Remove unreliable URL pass-through strategy (image_url field)
- Always download and upload images as File objects
- Get MIME type from HTTP response headers for URLs
- Use File constructor (not just Blob) for proper multipart metadata
- Add comprehensive error logging with headers and file metadata
- Simplify to single reliable upload path

Fixes 400 'Upload a valid image' error caused by Blob not providing
proper filename/MIME metadata in multipart form data.
2025-12-21 05:19:33 +01:00
Giancarmine Salucci
856c5c26f4 revert(tandoor): change auth header back to Bearer
User's Tandoor instance uses Bearer token authentication (likely JWT)
rather than Django REST Framework's Token authentication.

Reverts authentication from 'Token' back to 'Bearer' to fix 403 error:
'Authentication credentials were not provided.'
2025-12-21 05:08:41 +01:00
Giancarmine Salucci
d1dc791854 fix(tandoor): implement smart image upload with auth fix
- Fix authentication header from 'Bearer' to 'Token' (DRF TokenAuth)
- Implement three-strategy upload system:
  1. URL pass-through for direct URLs (most efficient)
  2. Base64 data URL conversion for screenshots
  3. Fallback blob upload for any other format
- Add comprehensive error handling with response details
- Add detailed logging for debugging upload strategies
- Document thumbnail formats in extractThumbnailStealth()

Fixes #30 - Tandoor image upload 400 Bad Request error

Based on Tandoor source code analysis (cookbook/views/api.py):
- RecipeImageSerializer accepts 'image_url' field for server-side download
- Uses Token authentication, not Bearer
- Supports multipart file upload with proper MIME types
2025-12-21 04:58:45 +01:00
Giancarmine Salucci
f5a1089936 feat(parser): remove step number prefixes from recipe extraction
- Update RECIPE_EXTRACTION_PROMPT to v2.1
- Remove instruction to number steps sequentially
- Update OUTPUT FORMAT and both few-shot examples
- Remove 'All steps numbered sequentially' from quality checklist
- Update fallback parser system prompt in parseRecipeWithStandardCompletion
- Frontend <ol> element already handles auto-numbering
- Tandoor integration unaffected (uses array index for step numbers)

Fixes double-numbering bug where steps appeared as '1. 1. Step text'
All 34 tests passing

Implementation follows execution plan in docs/plans/RemoveStepNumberPrefixes.md
Documented in docs/outcomes/RemoveStepNumberPrefixes.md
2025-12-21 04:46:38 +01:00
Giancarmine Salucci
2de5567682 fix(extraction): resolve progressCallback undefined errors
- Add progressCallback parameter to extractFromEmbeddedJSON and extractFromDOM
- Pass onProgress callback from extractWithStrategies to all strategies
- Fix legacy strategy to use correct callback variable name
- Verify extractViaGraphQL correctly returns null thumbnail

This fixes ReferenceError that was preventing all extraction methods from working.
All extraction strategies now properly emit thumbnail progress events via SSE.

Closes: FixProgressCallbackUndefinedErrors
2025-12-21 04:28:07 +01:00
Giancarmine Salucci
7e4d82de8d feat(share): refactor page and enhance thumbnail extraction
- Extract 8 reusable components from monolithic share page
- Add LLM health indicator with 30s polling
- Implement stealth thumbnail extraction with 4-method cascade
- Integrate real-time thumbnail preview component
- Reduce share page from 306 to ~140 lines
- Add comprehensive outcome documentation

Components:
- UrlInputSection: URL input and extraction trigger
- ProgressIndicator: Loading state display
- ExtractedTextViewer: Collapsible text preview
- RecipeCard: Recipe display with Tandoor integration
- ErrorState: Error handling UI
- LogViewer: System logs with color coding
- LlmHealthIndicator: LLM status with polling
- ThumbnailPreview: Real-time thumbnail display

Thumbnail Methods:
1. Meta tag extraction (og:image, twitter:image)
2. Video poster attribute
3. Instagram embedded JSON data
4. Screenshot fallback

Stories Completed:
- Story 1: Component extraction and refactoring
- Story 2: LLM health status indicator
- Story 3: Enhanced stealth thumbnail extraction
- Story 4: Thumbnail preview integration

Closes: RefactorSharePageAndEnhanceThumbnails
2025-12-21 04:18:38 +01:00
Giancarmine Salucci
da58263aba feat: refactor frontend and fix LLM extraction
- Fix critical await bug in extract-stream endpoint
- Add comprehensive logging to LLM and parser modules
- Implement fallback to standard completion for incompatible models
- Create enhanced v2.0 prompts with social media handling and few-shot examples
- Add LLM health check endpoint
- Decompose share page into 6 focused Svelte 5 snippets

Resolves LM Studio integration issues and improves code maintainability
2025-12-21 03:49:33 +01:00