- Add SortMode type (AlphaSort | TagSort | CourseSort) to Roux.Html
- Routes: / (alpha), /sorted/tags, /sorted/course
- AlphaSort: recipes sorted by title (from metadata) or filename
- TagSort: recipes grouped under tag headings, empty state when none
- CourseSort: recipes grouped by course/category metadata
- Navigation bar in index page with active-highlighted buttons
- Add metaCourse field to Metadata model (+ emptyMetadata update)
- Add nubOrd helper for deduplicating tag/course lists
- All 26 tests passing, hlint clean
- Html.hs: replace lambdas with section/composition where possible
- Html.hs: replace case/fromMaybe with fromMaybe
- Html.hs: add fromMaybe to imports, use unqualified
- Parser.hs: replace span with break (2 locations)
- Parser.hs: replace if/then/else with list comprehension
- Parser.hs: remove redundant catch-all patterns (break is exhaustive)
- Server.hs: eta reduce htmlResponse
- Add .hlint.yaml to suppress 'Avoid lambda' (suggested fix has
precedence issues with blaze-html's infix (!) operator)
- All 26 tests passing, hlint: No hints, exit code 0
- Add Roux.Html.recipePage: full recipe detail page with:
- Back navigation link
- Title and metadata (servings, times, difficulty, etc.)
- Ingredients summary list with quantities
- Sections with named headings
- Steps with inline element rendering:
- Ingredients highlighted in pumpkin (with quantity badge)
- Cookware in violet italic
- Timers in azure with ⏱ icon
- Comments in sand italic
- Recipe refs as links
- Line breaks
- Update Roux.Server: route /recipes/FILENAME to recipe page
- Case-insensitive filename lookup, .cook extension optional
- URL decoding for filenames
- Fix ingredient name parsing: restrict name characters to
alphaNum + spaces/hyphens/apostrophes, preventing greedy
consumption across special chars like ) and #
- Split name chars into pMultiNameChar (with spaces, before braces)
and pSingleNameChar (without spaces, fallback)
- Suppress -Wname-shadowing in Html.hs (blaze-html exports clash
with common variable names)
- All 26 tests passing, server smoke-tested with examples
- Add splitAmountUnit to parse quantities with unit attached without
% separator (e.g., @water{1,1/2cups}, @gelatine{3tsp})
- Add isNumericChar helper for detecting numeric quantity characters
- Add compound quantity test (1,1/2 cups = 3/2)
- Add no-separator unit test (3tsp)
- All 26 tests passing, all 4 example files parse correctly
- Fix pRecipeRef to include ./ prefix in stored path
- Add recipe reference test
- Add parse-validation tests for all 4 example .cook files
(EasyPancakes, FriedRice, CoffeeSouffle, OlivierSalad)
- All 24 tests passing, all examples parse without error
- Add pEndComment parser for -- end-of-line comments
- Add pInlineComment parser for [- ... -] block comments
- Update pText to stop before -- and [- sequences using notFollowedBy
- Add tests for both comment types (2 new tests, 19 total)
- Update Roux.Types: add SectionBodyItem (SecStep/SecComment/SecNote),
StepEndComment, remove StepNote (notes are now section-level)
- Rewrite Roux.Parser using Parsec for inline elements, text-splitting
for file-level structure
- Handle full-line -- comments, > notes, = Name = section headers
- Handle = Name (w/ and w/o closing =), == Name == variations
- Handle end-of-line backslash line breaks within steps
- Update tests to match new model + 3 EasyPancakes integration tests
- Add expectTextStep helper at module level
- Create Roux.Parser with parseCookFile (splits paragraphs, treats
each as a plain-text step, no metadata parsing yet)
- Add emptyMetadata to Roux.Types for convenience
- Add test dependencies: hspec, tasty-golden, tasty-hspec
- Add ParserSpec with 4 tests covering single step, multiple steps,
empty input, and whitespace trimming
- Wire ParserSpec into test runner via tasty-hspec