Commit Graph

137 Commits

Author SHA1 Message Date
jbrechtel 4dd580a042 Formats repo
Build and Deploy / build-and-deploy (push) Successful in 13m25s
2026-05-26 06:45:02 -04:00
jbrechtel ee42bfd981 fix: darken text colors — define --roux-muted, add explicit color to Cancel button, darken recent entry comment/date colors
Build and Deploy / build-and-deploy (push) Failing after 9s
2026-05-26 06:44:23 -04:00
jbrechtel 92ec058114 feat: show most recent 5 cook entries under Record meal button
Build and Deploy / build-and-deploy (push) Failing after 1m17s
2026-05-26 06:40:24 -04:00
jbrechtel 14f43a4ae9 fix: send URL-encoded form data instead of multipart, show errors inline, default empty date to today
Build and Deploy / build-and-deploy (push) Successful in 15m46s
2026-05-25 22:26:53 -04:00
jbrechtel 930fba369c feat: replace inline cook log form with 'Record meal' button + modal
Build and Deploy / build-and-deploy (push) Successful in 17m20s
2026-05-25 22:20:00 -04:00
jbrechtel ac7a979fe9 Formats repo
Build and Deploy / build-and-deploy (push) Successful in 15m3s
2026-05-25 22:11:28 -04:00
jbrechtel 0b6b9c3d17 fix: use Text for created_at, fix show-bug in ToJSON, add /cook-history route
Build and Deploy / build-and-deploy (push) Failing after 21s
2026-05-25 22:10:23 -04:00
jbrechtel 90971568a3 feat: add cook history nav link to index page 2026-05-25 22:01:24 -04:00
jbrechtel c6a9c2dee4 feat: wire up cook log DB at server startup 2026-05-25 22:00:45 -04:00
jbrechtel ceadf50c59 feat: add CookLog module with types and DB operations 2026-05-25 21:55:43 -04:00
jbrechtel 0b5fa61acd chore: add sqlite-simple dependency 2026-05-25 21:54:22 -04:00
jbrechtel edfa9bac76 docs: add implementation plan for cooking log feature 2026-05-25 21:48:51 -04:00
jbrechtel f2bd667b75 docs: add cooking log design spec 2026-05-25 21:45:49 -04:00
jbrechtel ed015d3799 Fix all 20 hlint warnings across 5 source files
Build and Deploy / build-and-deploy (push) Successful in 12m38s
- Use newtype instead of data for single-field types (RouxConfig, ImportError)
- Replace maybe "" id with fromMaybe "" throughout
- Replace not (x `elem` ys) with x `notElem` ys
- Remove redundant brackets, redundant $, and eta-reduce lookupRecipe
- Use numeric underscore for 1_000_000
2026-05-25 21:30:21 -04:00
jbrechtel 67c3b4df6c Merge branch 'feat/inline-title-edit'
Build and Deploy / build-and-deploy (push) Successful in 2m10s
2026-05-21 21:37:20 -04:00
jbrechtel 69f4184f2e feat: add PATCH endpoint for recipe title updates 2026-05-21 21:31:33 -04:00
jbrechtel 7b51c617de feat: add inline title editing JavaScript 2026-05-21 21:29:54 -04:00
jbrechtel e5ed706aa6 Updates prompt 2026-05-21 21:29:34 -04:00
jbrechtel 1f7b55af6f feat: add CSS class and styles for editable recipe title 2026-05-21 21:29:20 -04:00
jbrechtel 8e6e572b35 chore: add .worktrees to gitignore 2026-05-21 21:28:03 -04:00
jbrechtel 59fd96b57d docs: add design and plan for inline title editing 2026-05-21 21:28:00 -04:00
jbrechtel d64129fc9f fix: save recipe images to recipe-images/ subdirectory
Build and Deploy / build-and-deploy (push) Successful in 1m49s
Previously images were saved directly in the recipe directory
alongside .cook files. Now they go into <recipes-dir>/recipe-images/,
keeping the recipe directory clean and making the purpose of the
file clear from its path.
2026-05-21 21:06:07 -04:00
jbrechtel a509f44300 feat: add recipe image upload with in-place metadata update
Build and Deploy / build-and-deploy (push) Successful in 2m4s
Users can now upload an photo for any recipe directly from the
view page. The upload button appears as either a 'Replace' overlay
on existing images or an empty dashed placeholder for recipes
without one.

Upload flow:
1. Click upload button -> file picker opens (accepts image/*)
2. JavaScript reads the file as base64, POSTs to /upload-image
   with form fields: filename, file-b64, file-name
3. Server decodes the base64, saves to <recipe-dir>/<basename>.<ext>
4. Server updates the .cook file's YAML front matter, adding or
   updating the 'image:' key with a /recipe-images/ URL
5. Server returns JSON success, page reloads to show the image

Server routes added:
- POST /upload-image  — accepts base64-encoded image upload
- GET /recipe-images/<filename> — serves saved recipe images
  (extension-whitelisted to .jpg/.jpeg/.png/.gif/.webp)

Image files are stored alongside .cook files in the recipe directory
and served via the /recipe-images/ path. The fsnotify watcher detects
the .cook metadata change and triggers an SSE reload.
2026-05-21 20:43:58 -04:00
jbrechtel 76fab5c716 fix: save uploaded files to recipe-images/ subdir and inject source URL
Build and Deploy / build-and-deploy (push) Successful in 1m49s
2026-05-21 17:04:34 -04:00
jbrechtel 439f5780c9 feat: show recipe image thumbnail on index page
Build and Deploy / build-and-deploy (push) Successful in 1m43s
2026-05-21 15:19:14 -04:00
jbrechtel 2dd8840569 fix: resolve prompts dir in Docker and script, try multiple locations
Build and Deploy / build-and-deploy (push) Successful in 1m34s
2026-05-21 12:58:52 -04:00
jbrechtel f6040f5445 chore: add convert-to-cooklang and Python deps to Dockerfile
Build and Deploy / build-and-deploy (push) Successful in 4m21s
2026-05-21 11:59:31 -04:00
jbrechtel 5117c9d445 fix: remove relative path fallback in findConvertScript, rely on PATH only
Build and Deploy / build-and-deploy (push) Successful in 1m33s
2026-05-21 11:42:06 -04:00
jbrechtel 16074b0060 fix: resolve convert-to-cooklang script path with makeAbsolute
Build and Deploy / build-and-deploy (push) Successful in 1m46s
2026-05-21 11:15:39 -04:00
jbrechtel 76fb927109 Fixes typo in Dockerfile config
Build and Deploy / build-and-deploy (push) Successful in 1m43s
2026-05-21 11:03:10 -04:00
jbrechtel d9a7f6538e feat: add Import link to index page navbar
Build and Deploy / build-and-deploy (push) Successful in 1m32s
2026-05-21 10:56:35 -04:00
jbrechtel c36e632d25 style: style import form inputs to match search box
Build and Deploy / build-and-deploy (push) Successful in 1m32s
2026-05-21 10:52:34 -04:00
jbrechtel 5680cc434c Loads config in Docker
Build and Deploy / build-and-deploy (push) Successful in 1m35s
2026-05-21 10:48:52 -04:00
jbrechtel e5e0f1c0e0 chore: add yaml and base64-bytestring dependencies, re-export Config
Build and Deploy / build-and-deploy (push) Successful in 2m1s
2026-05-21 10:34:03 -04:00
jbrechtel 232373cd5c feat: expand import form with file upload via base64 JS 2026-05-21 10:32:49 -04:00
jbrechtel 578d1ae068 feat: add LLM import pipeline with file upload and text input 2026-05-21 10:29:35 -04:00
jbrechtel b179135136 feat: add --config-file CLI flag for YAML config 2026-05-21 10:25:06 -04:00
jbrechtel c015dce51e fix: strip code fences, add MIME/size validation, add tests 2026-05-21 10:23:25 -04:00
jbrechtel 7a3f63e377 feat: add convert-to-cooklang script for Anthropic API recipe conversion 2026-05-21 10:18:23 -04:00
jbrechtel 5765829004 refactor: return Either from loadConfig, add Config tests 2026-05-21 10:16:41 -04:00
jbrechtel 1d5459ea1e feat: add Config module for YAML config loading 2026-05-21 10:10:59 -04:00
jbrechtel 5abf5490b4 feat: add interactive countdown timers to recipe steps
Build and Deploy / build-and-deploy (push) Successful in 1m45s
Cooklang timer references (~{15%minutes}) now render as clickable
spans. Clicking one starts a live countdown displayed in-place
with MM:SS format. A reset button (↺) restarts the timer from
the original duration. A close button (✕) returns to the static
label. When the timer reaches zero, the display pulses and turns
rust-colored for 3 cycles.

Implementation:
- durationToSeconds helper converts Duration to total seconds
- Timer span uses data-seconds attribute for the total
- Label + controls (display, reset, close) structure pre-rendered
  in HTML, toggled via a .running CSS class
- Timer JS initializes all .roux-timer-tag elements on page load
- Each timer manages its own setInterval, multiple timers can
  run simultaneously across different steps
2026-05-20 22:50:35 -04:00
jbrechtel ddb8577c4b feat: add headless Chromium fallback to recipe scraper
Build and Deploy / build-and-deploy (push) Has been cancelled
Uses Playwright to drive Chromium when the primary requests-based
fetch fails (e.g., Cloudflare, JS-required pages). Hybrid approach:
try fast path first, fall back to Chromium only on failure.

Dockerfile: add playwright==1.52.0 pip package, run playwright
install-deps chromium + playwright install chromium to download
the browser and all system libraries.

scrape-recipe: add fetch_with_chromium() that tries Playwright
first, then chromium --headless --dump-dom as a subprocess fallback.
Modify main() to catch primary fetch errors and call Chromium
fallback before giving up.
2026-05-20 22:44:15 -04:00
jbrechtel 8807ba3851 feat: show recipe image alongside description when metadata has one
Build and Deploy / build-and-deploy (push) Successful in 1m30s
Adds a .roux-desc-row flex container that places the recipe
description text on the left and the image (from metaImage)
on the right, using what was previously empty horizontal space.
Image is capped at 280px, with a subtle shadow and rounded
corners. Responsively stacks on mobile.
2026-05-20 22:35:25 -04:00
jbrechtel 1b7f7b61aa fix: tighten typography, fix parser brace leak, polish UI
Build and Deploy / build-and-deploy (push) Successful in 1m40s
- Drop italics on cookware/timer tags — they were over-emphasizing
  roughly a third of step text. Now plain with slight opacity.
- Fix parser bug: @salt{large pinch} braces no longer leak through
  as raw text. Non-numeric quantities are consumed silently,
  ingredient appears without displayed quantity.
- Remove duplicate '\342\206\227 Original' link from marginalia SOURCE block.
  Title-adjacent link is sufficient.
- Add tags to marginalia column for visual balance.
- Constrain content to 1120px max-width for a printed-cookbook feel.
- Add .container h2 CSS specificity override to beat Pico CSS defaults.
  Section labels (INGREDIENTS, METHOD) now properly render in Fraunces.
- Switch navbars from <div> to <nav> so Pico's own nav ul{list-style:none}
  suppresses gray square bullets. Add explicit li{list-style:none} override.
- Constrain content to 1120px max-width so parchment frames the page.
2026-05-20 22:31:23 -04:00
jbrechtel 752918c1af fix: remove extra closing brace in index page JS
Build and Deploy / build-and-deploy (push) Successful in 1m26s
renderByCourse had a duplicate '}' that caused
'SyntaxError: missing ) in parenthetical' on load.
2026-05-20 22:18:17 -04:00
jbrechtel 78764be0a9 refactor(ui): overhaul typography, color palette, and recipe page layout
Build and Deploy / build-and-deploy (push) Successful in 1m35s
Changes based on design feedback:

Typography: Swap Lexend Peta → Fraunces (literary serif) for all
headings, preserving Quicksand for body text.

Color palette: Add --roux-ochre and --roux-sage vars. Restrict rust
accent to step numbers, rules, and hover. Titles and labels → ink.

Navbar: 'Roux' logo now uses Fraunces in ink color.

Parser bug: Add isMethodSection filter to exclude 'Ingredients'
sections from the method column, preventing raw text ingredient
listings from leaking into steps.

Index page: Titles in ink with rust-on-hover. Add letter dividers
(A, B, C...) for alpha mode. Show tags/course metadata under titles.

Recipe page: 3-column grid (240px 1fr 180px) with marginalia column
for source link and notes. Tag pills → sage outline. Ingredient
highlights → ochre underline. Checkboxes → hand-sketched rotation.
Source link → muted ink with rust hover.
2026-05-20 22:13:33 -04:00
jbrechtel bf6fde376a feat: parse schema.org ingredient strings into structured Cooklang ingredients
Build and Deploy / build-and-deploy (push) Successful in 1m45s
Replaces buildIngredientSection's plain-text dump with proper
StepIngredient annotation. Parses ingredient strings like
"3/4 cup extra-virgin olive oil" into:
  - quantity: 3/4
  - unit: "cup"
  - name: "extra-virgin olive oil"

Handles fractions ("3/4"), compound quantities ("1,1/2"),
unit recognition (cup, ounce, pound, etc.), and strips
preparation notes after the first comma.
2026-05-20 18:11:30 -04:00
jbrechtel d29d8b0af0 fix: separate steps with blank lines in Cooklang renderer
Build and Deploy / build-and-deploy (push) Successful in 1m17s
The Cooklang parser (splitByBlankLines) identifies step boundaries by
blank lines (\n\n). renderSection was joining body items with a single
newline (\n), causing multiple steps to be parsed as one. Fixed by using
\n\n as the separator between body items, matching the spec.
2026-05-20 16:04:16 -04:00
jbrechtel c33a51d80b test: add step count preservation tests for SchemaOrg → Cooklang conversion
Build and Deploy / build-and-deploy (push) Successful in 1m14s
Adds checkStepCount helper that verifies the number of instructions in
the parsed schema.org recipe matches the number of SecStep items in
the Method section of the resulting Cooklang Recipe. Tests both
fried-rice (4 steps) and carrot-risotto (7 steps).
2026-05-20 15:57:14 -04:00