Commit Graph

89 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
91aca8d35a ci: trigger rebuild with registry secrets configured
All checks were successful
Build & Push Docker Image / test-and-build (push) Successful in 1m42s
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-12 22:14:56 +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
97355d859f ci: multi-stage Dockerfile + fix workflow to match runner pattern
Some checks failed
Build & Push Docker Image / test-and-build (push) Failing after 38s
- Add tester/builder/runner stages to Dockerfile
- Use docker/build-push-action@v6 (matches other repos)
- Run tests via Docker tester stage (no setup-node needed)
- Fix secret names: REGISTRY_USERNAME / REGISTRY_TOKEN
- Runner target: keep yt-dlp + Chromium, copy only build artifacts

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-12 20:53:35 +02:00
Giancarmine Salucci
b4edfe2ac1 Merge feature/RECIPE-0009_deduplication_notifications_ui
Some checks failed
Build & Push Docker Image / test-and-build (push) Failing after 31s
RECIPE-0009: deduplication, notifications, UI improvements
- Iteration 0: deduplication, push notification subscribe, UI
- Iteration 1: footer status bar, icon-only buttons
- Iteration 2: ARIA-compliant footer icon contrast

yt-dlp extractor:
- Replace Playwright scraper with yt-dlp subprocess
- Feature flag EXTRACTOR_BACKEND (ytdlp|playwright)
- Dockerfile: add yt-dlp via pip3

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-12 20:50:52 +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
c98a2407a7 chore(RECIPE-0009): update FINDINGS.md for iteration 1 planning 2026-02-18 10:15:43 +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
40e3fb0c1b fix playwright 2026-02-18 01:43:41 +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
c752db36f7 Merge branch 'fix/RECIPE-0006_fix_recipe_extraction' 2026-02-17 19:52:49 +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
33d2a10f8e Merge branch 'fix/RECIPE-0006_fix_recipe_extraction' 2026-02-17 10:24:58 +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
e749763911 delete outdated docs 2026-02-16 22:40:52 +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
08425067e7 updated auth 2026-02-16 14:44:44 +01:00
Giancarmine Salucci
3810d0e401 feat(RECIPE-0002): complete iteration 0 — generate PWA icons and update manifest 2026-02-16 12:19:49 +01:00
Giancarmine Salucci
4f1ebcbac3 updated auth 2026-02-16 11:24:30 +01:00
Giancarmine Salucci
c97bbd3259 refresh auth 2026-02-16 10:02:47 +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
a6d50a6f4b untrack shit 2026-02-15 02:41:30 +01:00
Giancarmine Salucci
cc8d46660a new agents 2026-02-15 02:41:07 +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
621e113537 docs: add execution plan for fixing push notifications and enhancing PWA experience 2025-12-22 05:59:49 +01:00