- Add Import link to all page headers (landing, recipe, cook history, import)
- Match ingredient list font size to recipe method text (both 15px)
- Show recipe titles instead of filenames on Cook History page
- Make clicking anywhere on recipe hero image/placeholder trigger file upload dialog
- Change rp-body-grid from 1.5fr 1fr (content|history) to 1fr 1fr
(ingredients|method)
- Move cook history to full-width section below the grid
- Move sections list binding out of inner div so both columns can use it
- Darken step text: use --roux-text instead of #4A3525, explicit p color
to override Pico CSS defaults
- Make ingredient list interactive: each ingredient is now a checkbox
row that shows ✓ checkmark on click with line-through + muted opacity
- Collect ingredients from ALL sections (not just sections named
'Ingredients'), deduplicated per section, grouped by section name
- Switch from <ul>/<li> to <div>/<label> with checkbox inputs
When the trigger JS was updated to use querySelectorAll('.roux-record-trigger')
for dual-button support, the variable was renamed from 'btn' to 'btns' but the
submit handler still referenced 'btn'. This commit:
- Adds activeBtn tracking: the button that opened the modal is stored in
activeBtn via 'this' in the click handler
- Uses activeBtn.dataset.filename in the submit handler (with null guard)
- Removes the stale btn reference
- Card images now show the recipe's actual image when available
(background-image: url(...) with background-size: contain to preserve
aspect ratio without distortion), falling back to gradient placeholders
- Cards are now wrapped in <a href="/recipes/{filename}"> so clicking
anywhere on a card navigates to the recipe page
- Added .contain CSS class for image-fit behavior and .lnd-card a for
link styling
- New landing page (/) based on roux-landing-mockup.html layout
- Shuffle card that suggests random recipes
- Recently cooked shelf (driven by cook log DB)
- Recently added shelf (by file modification time)
- Existing recipe index moved to /recipes
- Updated nav links throughout to point to /recipes
- All hlint warnings fixed
- 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
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.
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.