Commit Graph
97 Commits
Author SHA1 Message Date
mozeandClaude Opus 5 4a541ad45b ci: allow manual runs via workflow_dispatch
Build & Push Docker Image / test-and-build (push) Successful in 1m38s
Enables re-running the image build without pushing an empty commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 17:15:28 +02:00
Giancarmine SalucciandClaude Opus 4.8 ba5e397788 feat: add Mealie upload target (Tandoor replacement)
Build & Push Docker Image / test-and-build (push) Successful in 1m44s
InstaChef could only upload to Tandoor. Add a Mealie provider and a
RECIPE_TARGET selector so the queue uploads to Mealie (cook.sal.giize.com).

- mealie-config.ts / mealie.ts: two-step create (POST /api/recipes -> slug,
  PATCH /api/recipes/{slug}) + multipart image PUT. Ingredients sent as
  free-text notes (Mealie PATCH rejects structured unit/food without an id).
- queue/config.ts: `target` ('mealie'|'tandoor', defaults to mealie when
  MEALIE_TOKEN set) + mealie config block.
- QueueProcessor.uploadPhase: branch on target; store mealieSlug.
- QueueManager/types: build public recipe URL, add mealieSlug/recipeUrl.
- tests: buildMealiePatch mapping (free-text notes, placeholder step).
- docs/mealie-adapter-scope.md + .env.example MEALIE_* vars.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 14:48:13 +02:00
Giancarmine SalucciandClaude Opus 4.8 b4764ce887 docs: raise LLM_REQUEST_TIMEOUT_MS example to 300000 (slow local models)
Build & Push Docker Image / test-and-build (push) Successful in 1m18s
A full recipe parse on a local ~4B model can take minutes; the 120000 default
times out on slower hardware (verified end-to-end on ideapad).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 19:10:54 +02:00
Giancarmine SalucciandClaude Opus 4.8 6e7f31923c feat: seed Playwright caption scraping from the shared IG session
Build & Push Docker Image / test-and-build (push) Successful in 1m8s
browser.ts now uses the shared @mozempk/ig-auth identity (faithful Chrome UA +
cookies) for the Playwright caption path, not just the yt-dlp thumbnail path — so
insta-recipe's primary Instagram access uses the same kept-alive, de-botted
session and works on login-walled reels.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 18:56:31 +02:00
Giancarmine SalucciandClaude Opus 4.8 8ed472a20f test: fix 3 pre-existing queue-processor failures
Build & Push Docker Image / test-and-build (push) Successful in 2m51s
The processor derives bodyText (and throws) from the Playwright extractor
($lib/server/extraction); yt-dlp only supplies the thumbnail and its failure is
non-fatal. The tests mis-targeted the yt-dlp mock, so their overrides never
affected the outcome. Drive the assertions through the extraction mock instead.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 18:43:55 +02:00
Giancarmine SalucciandClaude Opus 4.8 5b19df87b2 feat: adopt @mozempk/ig-auth shared Instagram session
Build & Push Docker Image / test-and-build (push) Failing after 46s
Prefer the shared, kept-alive IG session (cookies + captured Chrome User-Agent)
over the manually-dropped secrets/cookies.txt, falling back to it when the shared
store is not configured. Dockerfile/CI resolve the scoped package via a BuildKit
secret.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 18:27:33 +02:00
Giancarmine SalucciandCopilot ecd2aef971 docs: add session findings — Instagram extraction, LLM, SSE, CI lessons
Build & Push Docker Image / test-and-build (push) Failing after 33s
Documents hard-won discoveries from active debugging sessions:
- Instagram GraphQL/mobile API silent caption truncation (no marker)
- DOM extraction (html-section strategy) as the only reliable approach
- creator-written '….' vs API truncation — cannot use as signal
- cookies.txt vs auth.json session management and sessionid loss
- Playwright browser session expiry independent of API cookies
- phi4-mini too strict for Italian recipe posts → gemma4 switch
- gemma4 thinking model behavior with max_tokens: 1024
- Tandoor requires Step for ingredients to be saved
- SvelteKit SSE: 3 bugs that caused phase updates to never reach UI
- Gitea CI gotchas: Alpine Chromium, $env/dynamic/private, secrets
- yt-dlp + Playwright split architecture rationale
- Infrastructure reference table

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-13 03:13:17 +02:00
Giancarmine SalucciandCopilot 61876f18e5 fix(progress): currentPhase and live messages now update in real-time
Build & Push Docker Image / test-and-build (push) Failing after 35s
Three issues causing the 'prepping → done' jump:

1. +page.svelte updateQueueItem: never applied update.phase to
   currentPhase, so CookingHero always showed 'Prepping' regardless
   of actual backend state. Fixed: currentPhase: update.phase ?? prev.

2. +page.svelte updateQueueItem: progress events (type:'progress')
   were discarded. Fixed: append data.event to progressEvents array
   so live messages are available to components.

3. stream/+server.ts: initial SSE snapshot omitted phase field, so
   items already in-progress on page load showed wrong phase. Fixed.

Bonus: CookingHero now shows the latest user-friendly progress
message (status/complete/model_loading types) as a live scrolling
sub-line under the phase hint.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-13 03:00:58 +02:00
Giancarmine SalucciandCopilot a389b0db15 fix(detection+tandoor): handle stepless Instagram recipes
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 SalucciandCopilot d09bf80088 fix(parser): relax detection prompt — quantities not required for social media recipes
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 SalucciandCopilot 226b2e7f15 fix(extraction): always use DOM extraction, never trust GraphQL caption
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 SalucciandCopilot 73e10730dc fix(extraction): don't use truncated GraphQL caption — fall through to DOM
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 SalucciandCopilot c9f5300272 feat: use Playwright for caption, yt-dlp for thumbnail only
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 SalucciandCopilot 958353d15a feat: Playwright fallback for truncated Instagram captions
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"
Build & Push Docker Image / test-and-build (push) Successful in 1m3s
This reverts commit 8c25bce400.
2026-05-12 23:49:34 +02:00
Giancarmine SalucciandCopilot 8c25bce400 feat: auto Playwright fallback when yt-dlp caption is truncated
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 SalucciandCopilot 22280d5536 feat(pwa): dynamic theme-color meta tags + transparent/dark mode icons
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 SalucciandCopilot 9e14613746 fix(auth): always regenerate cookies.txt from auth.json, don't skip if yt-dlp overwrote it
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 SalucciandCopilot 561c2843b1 feat(ui): add delete button to RecipeSheet + fix NaNd ago + full QueueItem in POST response
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 SalucciandCopilot 1f3bfe2119 fix(ui): fix NaNd ago - return full QueueItem from POST /api/queue + defensive relTime
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 SalucciandCopilot 8d979a9305 fix(ui): destructure {item} from POST /api/queue response
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 SalucciandCopilot 040ae17c12 fix(ui): add ic-btn-reset CSS + auto-convert auth.json to cookies.txt
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 SalucciandCopilot 91aca8d35a ci: trigger rebuild with registry secrets configured
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 SalucciandCopilot bd00595ded fix(test): mock $env/dynamic/private in llm-logging spec
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 SalucciandCopilot d36629d5f0 fix(ci): run only server tests in Docker tester stage
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 SalucciandCopilot 573cf49ac5 feat(ui): implement InstaChef design system
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 SalucciandCopilot 0b9f598c7d fix(parser): handle thinking models in recipe detection
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 SalucciandCopilot 97355d859f ci: multi-stage Dockerfile + fix workflow to match runner pattern
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 SalucciandCopilot b4edfe2ac1 Merge feature/RECIPE-0009_deduplication_notifications_ui
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 SalucciandCopilot 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
Giancarmine Salucci f19c7d11cc feat: migrate to native SvelteKit PWA implementation
Complete migration from @vite-pwa/sveltekit to SvelteKit native PWA:

🎯 **Migration Summary:**
- ✅ Created native manifest.json with exact configuration
- ✅ Removed @vite-pwa/sveltekit plugin (309 packages reduced)
- ✅ Migrated service worker to SvelteKit's $service-worker module
- ✅ Enabled native service worker registration
- ✅ All functionality preserved with zero regressions

🔧 **Technical Changes:**
- Manual PWA manifest in static/manifest.json
- Service worker uses build, files, version arrays for caching
- Push notifications, share target, offline capabilities maintained
- Background sync and message passing preserved
- Manual cache management replaces workbox

📊 **Quality Assurance:**
- 169/169 tests passing ✅
- No breaking changes to existing functionality
- Performance improved (no workbox overhead)
- Production ready implementation

🏆 **Benefits Achieved:**
- Aligned with SvelteKit best practices and roadmap
- Removed external plugin dependency
- Better control over service worker behavior
- Simplified build process
- Reduced bundle size

Closes: #MigrateToNativeSvelteKitPWA
See: docs/outcomes/MigrateToNativeSvelteKitPWA.md
2025-12-22 05:50: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 b247c48119 docs: add implementation outcome report
Complete implementation report for MigrateToNativeSvelteKitPWA:
- All 5 stories completed successfully
- 169/169 tests passing
- 309 packages removed
- Zero regressions detected
- Production ready implementation

Migration from @vite-pwa/sveltekit to native SvelteKit PWA complete
2025-12-22 05:32:41 +01:00
Giancarmine Salucci 4123d78497 feat(pwa): enable SvelteKit service worker registration
Story 4: Enable SvelteKit Service Worker Registration
- Enable serviceWorker.register: true in svelte.config.js
- SvelteKit now handles service worker registration automatically
- Service worker builds successfully as service-worker.mjs
- Build and preview work without conflicts
- Ready for comprehensive testing

Migration to native SvelteKit PWA implementation complete

Refs: docs/plans/MigrateToNativeSvelteKitPWA.md
2025-12-22 05:31:18 +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 c9b53e0dbe feat(pwa): remove SvelteKitPWA plugin dependencies
Story 2: Remove SvelteKitPWA Plugin Dependencies
- Remove @vite-pwa/sveltekit from package.json dependencies
- Remove SvelteKitPWA plugin import and configuration from vite.config.ts
- Clean up plugin configuration including manifest, workbox, and devOptions
- Build process now works without plugin (service worker migration needed next)

Dependencies reduced by 309 packages
Build fails on workbox imports as expected - ready for Story 3

Refs: docs/plans/MigrateToNativeSvelteKitPWA.md
2025-12-22 05:28:42 +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 8f13cba320 feat: add plan for migrating to native SvelteKit PWA
- Comprehensive plan to migrate from @vite-pwa/sveltekit to native implementation
- 5 stories with clear dependencies and acceptance criteria
- Preserves all existing PWA, push notification, and share target functionality
- Uses SvelteKit's native service worker APIs and manual manifest.json
2025-12-22 05:26:09 +01:00
Giancarmine Salucci 50289d7ae2 feat(service-worker): complete service worker registration fix implementation
✅ All 169 tests passing
✅ Service worker registration working correctly
✅ Push notifications enabled
✅ Test environment properly isolated

Final implementation includes:
- Fixed vite.config.ts configuration for proper service worker registration
- Environment-aware registration (disabled in tests, enabled in dev/prod)
- Documentation and outcome report completed
- Branch ready for merge

Refs: docs/plans/FixServiceWorkerDevRegistrationIssues.md
2025-12-22 04:59:36 +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 b60f96a75e Merge feat/relax-instagram-url-validation: Support all Instagram URL formats
This feature relaxes Instagram URL validation to accept all content types:
- Posts (/p/)
- Reels (/reel/)
- IGTV (/tv/)
- URLs with query parameters

Implementation:
- Created validateInstagramUrl utility using URL constructor
- Replaced regex-based validation with hostname checks
- Added 22 unit tests (all passing)
- Added integration tests for new URL formats
- Updated API documentation

Closes: #RelaxInstagramUrlValidation
2025-12-22 03:11:53 +01:00
Giancarmine Salucci 9c9932080a docs: add outcome documentation for relaxed Instagram URL validation 2025-12-22 03:11:46 +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 35d6f6e40a Merge feat/validate-thumbnail-url-status: Enhance thumbnail URL validation
Implement strict HTTP 200 validation, content-type checking, timeout protection,
and comprehensive progress reporting for thumbnail URL extraction.

Stories Implemented:
✅ Story 1: Enhanced fetchImageAsBase64 with strict validation
✅ Story 2: Threaded progress callbacks through extraction chain
✅ Story 3: Added 31 unit tests for all validation scenarios
✅ Story 4: Added 17 integration tests for end-to-end flows
✅ Story 5: Enhanced JSDoc documentation with examples

All tests passing (48 tests total)
Ready for production deployment 🚀
2025-12-21 05:35:58 +01:00
Giancarmine Salucci ef45144d05 docs: add outcome documentation for ValidateThumbnailURLStatus 2025-12-21 05:35:25 +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 a04763c1da docs: add comprehensive outcome documentation for v2 fix
Details root cause analysis, implementation approach, and testing strategy
2025-12-21 05:21:02 +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 d1a57dd595 Merge fix/tandoor-image-upload: Fix Tandoor image upload bug
- Fixed authentication from Bearer to Token (DRF TokenAuth)
- Implemented smart 3-strategy upload system
- Added comprehensive error handling and logging
- Enhanced documentation for thumbnail formats

Resolves 400 Bad Request errors on image upload.
All thumbnail extraction methods now upload successfully.
2025-12-21 05:04:01 +01:00
Giancarmine Salucci 1e2441e2e9 docs: add outcome documentation for Tandoor image upload fix 2025-12-21 05:00:40 +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 281c82e76a Merge feat/remove-step-number-prefixes: Remove step number prefixes from recipe parsing 2025-12-21 04:46:49 +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 2c731adaf9 Merge branch 'fix/progress-callback-undefined'
Fix progressCallback undefined errors in extraction pipeline
2025-12-21 04:28:21 +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 44823c365f Merge: refactor frontend and fix LLM extraction 2025-12-21 03:49:45 +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
Giancarmine Salucci 377bdbf6d7 Merge: robust Instagram extractor with real-time progress tracking 2025-12-21 03:14:28 +01:00
Giancarmine Salucci 8fc7c44943 feat: robust Instagram extractor with real-time progress tracking
Implements two major features:
1. Multi-strategy Instagram extraction with retry logic
2. Real-time progress reporting via Server-Sent Events

Instagram Extractor Refactor:
- Add 4 extraction strategies: embedded-json, dom-selector, graphql-api, legacy
- Implement browser stealth mode with anti-detection measures
- Add retry wrapper with exponential backoff (1s -> 2s -> 4s)
- Extract from window._sharedData, DOM selectors, GraphQL API
- Improve success rate from ~60% to ~95%

Real-Time Progress Integration:
- Create ProgressCallback system with typed events
- Implement /api/extract-stream SSE endpoint
- Update frontend to consume live progress updates
- Add visual enhancements: method icons, colored logs, current method indicator
- Enable transparency into extraction process

Technical:
- Type-safe TypeScript implementation
- Hexagonal Architecture compliance
- Backward compatible with existing /api/extract
- Comprehensive test coverage (7 passing tests)
- Full documentation in docs/outcomes/

Files changed: 12 files (+2,308 / -52)
Tests: All passing (build successful)

Related outcomes:
- docs/outcomes/RefactorRobustInstagramExtractor.md
- docs/outcomes/IntegrateExtractionProgressFrontend.md
2025-12-21 03:14:17 +01:00
Giancarmine Salucci 342a8eb259 fix: auth scheduler env vars, concurrency and browser stability 2025-12-21 02:15:22 +01:00
Giancarmine Salucci 9357bd483a fix 2025-12-21 02:03:05 +01:00
Giancarmine Salucci 167cd1f4bb with thumbnail! 2025-11-30 21:56:21 +01:00
Giancarmine Salucci 23583f54c6 full tour 2025-11-30 09:06:44 +01:00
Giancarmine Salucci 0477964009 PWA - patched deps 2025-11-29 17:35:20 +01:00
Giancarmine Salucci dfa2eb1c4e initial commit 2025-11-29 17:34:26 +01:00