diff --git a/src/Roux/Html.hs b/src/Roux/Html.hs index 803ed1c..9dde13e 100644 --- a/src/Roux/Html.hs +++ b/src/Roux/Html.hs @@ -175,9 +175,11 @@ page title content = , "textarea.roux-import-input { font-family: monospace; font-size: 0.85rem; resize: vertical; }" , "/* Recipe list on index page */" , ".roux-recipes { list-style: none; padding: 0; }" - , ".roux-recipes li { padding: 0.4rem 0; }" - , ".roux-recipes a { font-size: 1rem; display: block; color: var(--roux-text); }" - , ".roux-recipes a:hover { color: var(--roux-accent); }" + , ".roux-recipes li { display: flex; align-items: flex-start; gap: 0.75rem; padding: 0.4rem 0; }" + , ".roux-recipes li a { font-size: 1rem; display: block; color: var(--roux-text); }" + , ".roux-recipes li a:hover { color: var(--roux-accent); }" + , ".roux-index-thumb { width: 48px; height: 48px; object-fit: cover; border-radius: 4px; flex-shrink: 0; margin-top: 0.1rem; }" + , ".roux-index-entry { min-width: 0; }" , ".roux-letter-divider { font-family: \"Fraunces\", serif; font-size: 1.1rem; font-weight: 500; color: var(--roux-text); margin: 1.2rem 0 0.25rem; padding-bottom: 0.2rem; border-bottom: 0.5px solid rgba(61, 44, 30, 0.08); }" , ".roux-recipe-meta { display: flex; gap: 0.4rem; flex-wrap: wrap; margin-top: 0.1rem; }" , ".roux-recipe-meta .r-tag { font-size: 0.65rem; color: rgba(61, 44, 30, 0.5); letter-spacing: 0.02em; }" @@ -312,7 +314,8 @@ searchJs = , " meta += '' + escapeHtml(r.course) + '';" , " }" , " let metaHtml = meta ? '
' + meta + '
' : '';" - , " return '
  • ' + escapeHtml(r.title) + '' + metaHtml + '
  • ';" + , " let imgHtml = r.image ? '\\\'\\\'' : '';" + , " return '
  • ' + imgHtml + '
    ' + escapeHtml(r.title) + '' + metaHtml + '
  • ';" , "}" , "" , "function renderFlatList(items) {" diff --git a/src/Roux/RecipeIndex.hs b/src/Roux/RecipeIndex.hs index 4159700..40a3961 100644 --- a/src/Roux/RecipeIndex.hs +++ b/src/Roux/RecipeIndex.hs @@ -61,6 +61,7 @@ data RecipeSearchEntry = RecipeSearchEntry { rseTitle :: !Text , rseFilename :: !Text , rseCourse :: !(Maybe Text) + , rseImage :: !(Maybe Text) , rseTags :: ![Text] } deriving stock (Eq, Show) @@ -73,6 +74,7 @@ recipeSearchEntrySchema = #+ required "title" rseTitle text #+ required "filename" rseFilename text #+ optionalNullable FC.EmitNull "course" rseCourse text + #+ optionalNullable FC.EmitNull "image" rseImage text #+ required "tags" rseTags (list text) {- | Convert a 'RecipeInfo' into a 'RecipeSearchEntry', extracting course and @@ -80,16 +82,18 @@ recipeSearchEntrySchema = -} toSearchEntry :: RecipeInfo -> RecipeSearchEntry toSearchEntry info = - let (course, tags) = case riRecipe info of + let (course, image, tags) = case riRecipe info of Right r -> ( metaCourse (recipeMetadata r) + , metaImage (recipeMetadata r) , metaTags (recipeMetadata r) ) - Left _ -> (Nothing, []) + Left _ -> (Nothing, Nothing, []) in RecipeSearchEntry { rseTitle = riTitle info , rseFilename = T.pack (riFilename info) , rseCourse = course + , rseImage = image , rseTags = tags }