require "kemal" require "json" require "markd" # or any markdown parser for crystal # Structure to hold recipe metadata struct Recipe property title : String property tags : Array(String) property file_path : String # explicit initializer to ensure all properties are set def initialize(title : String, tags : Array(String), file_path : String) @title = title @tags = tags @file_path = file_path end def self.load_from_folder(folder_path : String) recipes = [] of Recipe Dir.glob("#{folder_path}/*/*.md") do |file_path| content = File.read(file_path) # Extract front matter tags = [] of String body_content = "" if content.starts_with?("---") front_matter_end = content.index(/---\s*\n/, 4) # find the second "---" line if front_matter_end front_matter = content[4...front_matter_end].strip body_content = content[(front_matter_end + 4)..-1].strip tags = (front_matter.match(/tags:\s*\[(.*?)\]/)[1].split(",").map(&.strip) rescue [] of String) end else body_content = content title = content.lines[0].chomp.sub(/^# /, "") end # Render the markdown content without the tags html_content = Markd::Document.new(body_content).to_html # Append tags to the end in a readable format if !tags.empty? html_content += "
" end recipes << Recipe.new(title, tags, file_path, html_content) end recipes end # def self.load_from_folder(folder_path : String) # recipes = [] of Recipe # Dir.glob("#{folder_path}/*/*.md") do |file_path| # content = File.read(file_path) # title = content.lines[0].chomp.sub(/^# /, "") # tags = [] of String # add tag parsing logic if needed # recipes << Recipe.new(title, tags, file_path) # end # recipes # end end recipes = Recipe.load_from_folder("recipes") get "/" do categorized_recipes = recipes.group_by { |r| File.basename(File.dirname(r.file_path)).to_s } render "src/views/index.ecr", "src/views/layout.ecr" end # Route to display a specific recipe get "/recipes/:category/:recipe" do |env| category = env.params.url["category"] recipe = env.params.url["recipe"] recipe_path = "recipes/#{category}/#{recipe}.md" if File.exists?(recipe_path) markdown_content = File.read(recipe_path) html_content = Markd.to_html(markdown_content) render "src/views/recipe.ecr", "src/views/layout.ecr" else env.response.status_code = 404 "Recipe not found" end end # Route to display the search page get "/search" do |env| render "src/views/search.ecr", "src/views/layout.ecr" end # Route to handle search results by tag get "/search/results" do |env| query = env.params.query["tag"] || "" matched_recipes = recipes.select { |recipe| recipe.tags.includes?(query.downcase) } render "src/views/search_results.ecr", "src/views/layout.ecr" end Kemal.run