Web server with recipe index page
- Add Roux.Server: WAI application using wai+warp directly - Scans .cook files from recipe directory on startup - GET / → HTML index page listing all recipes - Other routes → 404 - Add Roux.RecipeIndex: directory scanning and recipe parsing - scanRecipes: lists .cook files, parses each, extracts titles - Falls back to filename when metadata title is absent - Add Roux.Html: blaze-html rendering with Pico CSS - indexPage: renders recipe list with links - urlEncode: percent-encodes filenames for URLs - Update Roux.hs: re-exports Server.app and Types - Add filepath to dependencies
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
{- | HTML rendering for the Roux web interface.
|
||||
|
||||
Uses blaze-html directly. Styling via Pico CSS (inlined in the page).
|
||||
-}
|
||||
module Roux.Html (
|
||||
indexPage,
|
||||
) where
|
||||
|
||||
import Data.ByteString.Lazy (ByteString)
|
||||
import Data.Text (Text)
|
||||
import qualified Data.Text as T
|
||||
import qualified Text.Blaze.Html.Renderer.Utf8 as R
|
||||
import Text.Blaze.Html5 as H
|
||||
import Text.Blaze.Html5.Attributes as A
|
||||
|
||||
import qualified Roux.RecipeIndex as Idx
|
||||
|
||||
-- | Render the recipe index page.
|
||||
indexPage :: [Idx.RecipeInfo] -> ByteString
|
||||
indexPage recipes =
|
||||
R.renderHtml $
|
||||
H.docTypeHtml $ do
|
||||
H.head $ do
|
||||
H.meta ! A.charset "utf-8"
|
||||
H.meta ! A.name "viewport" ! A.content "width=device-width, initial-scale=1"
|
||||
H.title "Roux — Recipes"
|
||||
H.link ! A.rel "stylesheet" ! A.href "https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"
|
||||
H.body $ do
|
||||
H.main ! A.class_ "container" $ do
|
||||
H.h1 "Roux"
|
||||
H.p "Your personal recipe collection."
|
||||
H.h2 "Recipes"
|
||||
H.ul $ mapM_ recipeItem recipes
|
||||
|
||||
-- | Render a single recipe list item.
|
||||
recipeItem :: Idx.RecipeInfo -> Html
|
||||
recipeItem info =
|
||||
H.li $ case Idx.riRecipe info of
|
||||
Left _err ->
|
||||
H.span ! A.class_ "error" $ do
|
||||
H.toHtml (Idx.riTitle info)
|
||||
" (parse error)"
|
||||
Right _recipe ->
|
||||
H.a ! A.href (H.toValue ("/recipes/" <> urlEncode (Idx.riFilename info))) $
|
||||
H.toHtml (Idx.riTitle info)
|
||||
|
||||
-- | Percent-encode a filename for use in a URL path segment.
|
||||
urlEncode :: FilePath -> Text
|
||||
urlEncode = T.pack . concatMap encodeChar
|
||||
where
|
||||
encodeChar c
|
||||
| c == ' ' = "%20"
|
||||
| c == '#' = "%23"
|
||||
| c == '%' = "%25"
|
||||
| otherwise = [c]
|
||||
Reference in New Issue
Block a user