The cook log SQLite DB was created at recipeDir/cook-log.db. In the Docker
deployment /recipes is mounted read-only, so SQLite could never create the
file there — the cook log has never worked in the container. The writable
/data volume was mounted but unused.
- initDb now creates the DB's parent directory if missing, so startup
initialization is self-contained and idempotent.
- app takes a dataDir and places cook-log.db there.
- Add optional --data-dir flag (defaults to the recipe dir, preserving
local-dev behavior); Docker CMD passes --data-dir /data.
Verified end-to-end as root against a read-only recipe dir and a
non-existent data dir: the dir and DB are created and cook-log POST/GET
round-trips.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
groupEntries was sorting month groups by reverse-alphabetical name
(e.g. 'June' before 'July'), producing oldest-first ordering.
Now sorts by the actual entry dates within each group (newest first).
Combined with the earlier DESC fix in fetchAllEntries, the cook
history page now shows newest months at top, newest entries within
each month at top.
fetchAllEntries had ORDER BY cooked_date ASC but the doc comment
claimed 'newest first'. Changed to DESC to match fetchEntries
behavior and fix the cook-history page (most recent at top).
- A-Z, By Tag, By Course now render as .roux-filter-tabs above the
search box, not in the navigation header
- Index page nav matches all other pages: Home | Recipes | Import | Cook History
- JS active-tab tracking targets filter links instead of nav links
- Add Home link to every page's navigation header (index, recipe,
cook history, import)
- Move landing page padding (36px 48px 80px) into base main.container
CSS so all pages share the same consistent spacing
- Remove per-page padding overrides from .lnd-page and .rp-page
- 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.