-
-
-
- {selectedTags.length > 0 && (
-
- )}
-
-
- {displayedTags.map((tag) => (
-
- ))}
-
- {tags.length > 10 && (
-
- )}
+
+
+
+
+
+
{hasActiveFilters && (
)}
-
+
);
}
diff --git a/data/README.md b/data/README.md
deleted file mode 100644
index 05aff1a..0000000
--- a/data/README.md
+++ /dev/null
@@ -1,140 +0,0 @@
-# Data Directory
-
-This directory contains all content and reference data for the cooking website.
-
-## Directory Structure
-
-```
-data/
-├── README.md # This file
-├── authors.json # Author profiles and information
-├── taxonomy.json # Categories, tags, and classification system
-└── recipes/ # Recipe content
- ├── README.md
- ├── examples/
- ├── appetizers/
- ├── mains/
- ├── desserts/
- ├── sides/
- ├── beverages/
- └── breads/
-```
-
-## Reference Files
-
-### authors.json
-
-Contains author profiles with biographical information, social links, and specialties.
-
-**Structure:**
-```json
-{
- "authors": [
- {
- "id": "pws",
- "name": "PWS",
- "fullName": "Paul Wilson Smith",
- "bio": "Short biography",
- "email": "email@example.com",
- "website": "https://...",
- "avatar": "/images/authors/pws.jpg",
- "social": {
- "github": "username"
- },
- "joinDate": "2026-01-01",
- "specialties": ["Italian cuisine", "Baking"],
- "favoriteIngredient": "Garlic"
- }
- ]
-}
-```
-
-**Usage in recipes:**
-- Reference the author by their `id` in the recipe frontmatter
-- Example: `author: "pws"`
-- The site will look up full author details from this file
-
-### taxonomy.json
-
-Defines the complete taxonomy system for organizing recipes.
-
-**Contains:**
-1. **Categories** - Main recipe classifications (appetizers, mains, desserts, etc.)
-2. **Tags** - Organized by type:
- - `cuisine` - Italian, Mexican, Asian, etc.
- - `protein` - Chicken, beef, vegetarian, etc.
- - `dietary` - Gluten-free, vegan, keto, etc.
- - `meal-type` - Breakfast, lunch, dinner, etc.
- - `occasion` - Weeknight, holiday, party, etc.
- - `cooking-method` - Baking, grilling, slow-cooker, etc.
- - `speed` - Quick-meals, one-pot, make-ahead, etc.
- - `flavor-profile` - Spicy, sweet, savory, etc.
- - `special` - Chocolate, pasta, soup, etc.
-3. **Difficulty** - Easy, medium, hard with descriptions
-
-**Usage in recipes:**
-- Use tag IDs from this file in recipe frontmatter
-- Example: `tags: ["italian", "chicken", "comfort-food"]`
-- Example: `category: "mains"`
-- Example: `difficulty: "medium"`
-- The site can validate tags against this taxonomy
-
-## Benefits of Reference Files
-
-### Consistency
-- Ensures uniform spelling and naming across all recipes
-- Prevents duplicate tags with slight variations
-- Maintains standardized author information
-
-### Validation
-- Can validate recipe frontmatter against these schemas
-- Catch typos and invalid values during build
-- Provide helpful error messages
-
-### UI Generation
-- Generate filter dropdowns from taxonomy
-- Display tag descriptions and icons
-- Show author bios and links automatically
-
-### Scalability
-- Easy to add new authors, tags, or categories
-- Central place to update descriptions and metadata
-- Supports multiple authors and contributors
-
-### SEO & Discovery
-- Structured data for search engines
-- Consistent taxonomy improves findability
-- Tag descriptions enhance metadata
-
-## Adding New Entries
-
-### Adding an Author
-
-1. Open `data/authors.json`
-2. Add a new author object to the `authors` array
-3. Ensure the `id` is unique and lowercase-hyphenated
-4. Add avatar image to `/public/images/authors/`
-5. Reference the author ID in recipe frontmatter
-
-### Adding a Tag
-
-1. Open `data/taxonomy.json`
-2. Find the appropriate tag category (cuisine, protein, etc.)
-3. Add a new tag object with `id`, `name`, and `description`
-4. Use the tag ID in recipe frontmatter
-
-### Adding a Category
-
-1. Open `data/taxonomy.json`
-2. Add a new category to the `categories` array
-3. Create the corresponding folder in `data/recipes/`
-4. Use the category ID in recipe frontmatter
-
-## Best Practices
-
-- Always reference tags/categories/authors by their `id`
-- Keep IDs lowercase with hyphens (kebab-case)
-- Write clear, helpful descriptions
-- Add emojis to categories for visual interest
-- Update both reference files and recipes together
-- Validate changes before committing
diff --git a/data/authors.json b/data/authors.json
deleted file mode 100644
index e8282ad..0000000
--- a/data/authors.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "authors": [
- {
- "id": "pws",
- "name": "PWS",
- "fullName": "Paul Wilson Smith",
- "bio": "Home cook and food enthusiast sharing tested recipes and cooking techniques.",
- "email": "pws@example.com",
- "website": "https://github.com/runyanjake/cooking",
- "avatar": "/images/authors/pws.jpg",
- "social": {
- "github": "runyanjake"
- },
- "joinDate": "2026-01-01",
- "specialties": ["Italian cuisine", "Baking", "Comfort food"],
- "favoriteIngredient": "Garlic"
- }
- ]
-}
diff --git a/data/recipes/README.md b/data/recipes/README.md
deleted file mode 100644
index a46cd40..0000000
--- a/data/recipes/README.md
+++ /dev/null
@@ -1,212 +0,0 @@
-# Recipe Content Structure
-
-This directory contains all recipe content for the cooking website.
-
-## Folder Organization
-
-```
-data/recipes/
-├── appetizers/
-├── mains/
-├── desserts/
-├── sides/
-├── beverages/
-└── breads/
-```
-
-Each recipe follows this structure:
-```
-category/
-└── YYYY.MM.DD-recipe-slug/
- ├── YYYY.MM.DD-recipe-slug.mdx
- └── assets/
- ├── hero.jpg
- ├── step1.jpg
- └── ...
-```
-
-## Recipe Format
-
-### MDX File Structure
-
-The `.mdx` file contains all recipe metadata and content in one place.
-
-#### Frontmatter (Required)
-
-All recipe metadata is stored in YAML frontmatter at the top of the MDX file:
-
-```yaml
----
-title: "Recipe Title"
-slug: "recipe-slug"
-date: "YYYY-MM-DD"
-lastUpdated: "YYYY-MM-DD"
-category: "mains"
-tags: ["tag1", "tag2", "tag3"]
-dietary: ["vegetarian", "gluten-free"]
-cookTime: 45
-prepTime: 20
-totalTime: 65
-difficulty: "easy"
-servings: 4
-author: "Author Name"
-description: "Short description for SEO and previews"
-featured: true
-display: true
-displayPhoto: "./assets/hero.jpg"
----
-```
-
-**Frontmatter Fields:**
-- `title` - Display title of the recipe
-- `slug` - URL-friendly identifier
-- `date` - Publication date (YYYY-MM-DD)
-- `lastUpdated` - Last modification date (YYYY-MM-DD)
-- `category` - Main category ID (references `../taxonomy.json`)
-- `tags` - Array of tag IDs (references `../taxonomy.json`)
-- `dietary` - Array of dietary tag IDs (references `../taxonomy.json`)
-- `cookTime` - Active cooking time in minutes
-- `prepTime` - Preparation time in minutes
-- `totalTime` - Total time in minutes
-- `difficulty` - Difficulty ID: easy, medium, or hard (references `../taxonomy.json`)
-- `servings` - Number of servings
-- `author` - Author ID (references `../authors.json`)
-- `description` - Brief description for SEO and cards
-- `featured` - Boolean for homepage featuring
-- `display` - Boolean to control visibility (set to false to hide recipe)
-- `displayPhoto` - Relative path to display photo (e.g., "./assets/hero.jpg")
-
-**Note:** Use IDs from the reference files (`data/authors.json` and `data/taxonomy.json`) to ensure consistency and enable validation.
-
-#### Content Sections
-
-The following `## ` (h2) sections are parsed into tabs in the UI:
-
-1. **## Photos** - Recipe images with captions
-2. **## Ingredients** - Lists of ingredients (can use h3 subsections)
-3. **## Instructions** - Step-by-step cooking instructions
-4. **## Notes** - Tips, variations, storage info (optional)
-5. **## References** - Sources, inspirations, credits (optional)
-
-#### Example MDX Structure
-
-```mdx
----
-title: "Recipe Name"
-slug: "recipe-name"
-date: "2026-02-08"
-lastUpdated: "2026-02-08"
-category: "mains"
-tags: ["italian", "chicken"]
-dietary: ["gluten-free-option"]
-cookTime: 45
-prepTime: 20
-totalTime: 65
-difficulty: "medium"
-servings: 4
-author: "PWS"
-description: "Short description for SEO and previews"
-featured: false
-display: true
-displayPhoto: "./assets/hero.jpg"
----
-
-# Recipe Name
-
-Introduction paragraph about the recipe.
-
-## Photos
-
-
-*Caption describing the image*
-
-
-*Another helpful image*
-
-## Ingredients
-
-### For the Main Component
-- 2 cups ingredient one
-- 1 tablespoon ingredient two
-- Salt and pepper to taste
-
-### For the Sauce
-- 1 cup sauce base
-- Seasonings
-
-## Instructions
-
-### Preparation
-1. **Step name**: Detailed instruction with technique.
-2. **Another step**: More details here.
-
-### Cooking
-3. **Heat and cook**: Continue with numbered steps.
-4. **Finish**: Final steps.
-
-## Notes
-
-### Tips for Success
-- Helpful tip one
-- Helpful tip two
-
-### Storage
-- How to store leftovers
-- Freezing instructions
-
-### Variations
-- How to adapt the recipe
-
-## References
-
-- Source credits
-- Inspiration mentions
-- Cookbook references
-```
-
-## Content Guidelines
-
-### Writing Style
-- Use clear, conversational language
-- Include helpful tips and context
-- Explain techniques for beginners
-- Add timing and temperature details
-
-### Photography
-- Include hero shot (main finished dish)
-- Add process shots for complex steps
-- Use descriptive alt text for accessibility
-- Optimize images (web-friendly sizes)
-
-### Tags
-Choose from these categories:
-- **Cuisine**: italian, mexican, asian, american, mediterranean, etc.
-- **Protein**: chicken, beef, pork, seafood, vegetarian, vegan
-- **Meal Type**: breakfast, lunch, dinner, snack, appetizer
-- **Occasion**: weeknight, holiday, party, comfort-food
-- **Dietary**: gluten-free, dairy-free, low-carb, keto, paleo
-- **Cooking Method**: baking, grilling, slow-cooker, instant-pot
-- **Speed**: quick-meals, one-pot, make-ahead, no-cook
-
-### Difficulty Levels
-- **easy**: Beginner-friendly, simple techniques, common ingredients
-- **medium**: Some cooking experience needed, multiple steps
-- **hard**: Advanced techniques, precise timing, specialty equipment
-
-## Adding New Recipes
-
-1. Create folder: `data/recipes/[category]/YYYY.MM.DD-recipe-name/`
-2. Create `YYYY.MM.DD-recipe-name.mdx` with frontmatter and content
-3. Add images to `assets/` folder
-4. Build locally to verify rendering
-5. Commit and push
-
-## Best Practices
-
-- Use consistent date formatting (YYYY.MM.DD)
-- Keep slugs URL-friendly (lowercase, hyphens)
-- Optimize images before adding (compress, resize)
-- Test recipes before publishing
-- Include metric and imperial measurements when possible
-- Credit sources and inspirations
-- Update featured flag sparingly (limit to 3-5 recipes)
diff --git a/data/taxonomy.json b/data/taxonomy.json
deleted file mode 100644
index b9242b1..0000000
--- a/data/taxonomy.json
+++ /dev/null
@@ -1,187 +0,0 @@
-{
- "categories": [
- {
- "id": "appetizers",
- "name": "Appetizers",
- "slug": "appetizers",
- "description": "Small dishes to start your meal",
- "icon": "🥖"
- },
- {
- "id": "mains",
- "name": "Main Dishes",
- "slug": "mains",
- "description": "Hearty entrees and main courses",
- "icon": "🍽️"
- },
- {
- "id": "desserts",
- "name": "Desserts",
- "slug": "desserts",
- "description": "Sweet treats and baked goods",
- "icon": "🍰"
- },
- {
- "id": "sides",
- "name": "Side Dishes",
- "slug": "sides",
- "description": "Complementary dishes and accompaniments",
- "icon": "🥗"
- },
- {
- "id": "beverages",
- "name": "Beverages",
- "slug": "beverages",
- "description": "Drinks, smoothies, and cocktails",
- "icon": "🥤"
- },
- {
- "id": "breads",
- "name": "Breads & Baking",
- "slug": "breads",
- "description": "Homemade breads, rolls, and baked goods",
- "icon": "🍞"
- }
- ],
- "tags": {
- "cuisine": {
- "name": "Cuisine",
- "description": "Regional and cultural cooking styles",
- "tags": [
- { "id": "italian", "name": "Italian", "description": "Italian cuisine and cooking traditions" },
- { "id": "mexican", "name": "Mexican", "description": "Mexican and Latin American dishes" },
- { "id": "asian", "name": "Asian", "description": "Asian cuisine including Chinese, Japanese, Thai, etc." },
- { "id": "american", "name": "American", "description": "Classic American cooking" },
- { "id": "mediterranean", "name": "Mediterranean", "description": "Mediterranean diet and cuisine" },
- { "id": "french", "name": "French", "description": "French culinary techniques and dishes" },
- { "id": "indian", "name": "Indian", "description": "Indian spices and cooking methods" },
- { "id": "middle-eastern", "name": "Middle Eastern", "description": "Middle Eastern flavors and dishes" }
- ]
- },
- "protein": {
- "name": "Protein",
- "description": "Main protein sources",
- "tags": [
- { "id": "chicken", "name": "Chicken", "description": "Chicken-based recipes" },
- { "id": "beef", "name": "Beef", "description": "Beef and red meat dishes" },
- { "id": "pork", "name": "Pork", "description": "Pork and ham recipes" },
- { "id": "seafood", "name": "Seafood", "description": "Fish and shellfish dishes" },
- { "id": "lamb", "name": "Lamb", "description": "Lamb-based recipes" },
- { "id": "vegetarian", "name": "Vegetarian", "description": "No meat, may include dairy and eggs" },
- { "id": "vegan", "name": "Vegan", "description": "Plant-based, no animal products" }
- ]
- },
- "dietary": {
- "name": "Dietary",
- "description": "Special dietary considerations",
- "tags": [
- { "id": "gluten-free", "name": "Gluten-Free", "description": "No gluten-containing ingredients" },
- { "id": "gluten-free-option", "name": "Gluten-Free Option", "description": "Can be made gluten-free with substitutions" },
- { "id": "dairy-free", "name": "Dairy-Free", "description": "No dairy products" },
- { "id": "low-carb", "name": "Low-Carb", "description": "Reduced carbohydrate content" },
- { "id": "keto", "name": "Keto", "description": "Ketogenic diet friendly" },
- { "id": "paleo", "name": "Paleo", "description": "Paleo diet compliant" },
- { "id": "whole30", "name": "Whole30", "description": "Whole30 program approved" },
- { "id": "nut-free", "name": "Nut-Free", "description": "No nuts or nut products" }
- ]
- },
- "meal-type": {
- "name": "Meal Type",
- "description": "When to serve this dish",
- "tags": [
- { "id": "breakfast", "name": "Breakfast", "description": "Morning meals and brunch" },
- { "id": "lunch", "name": "Lunch", "description": "Midday meals" },
- { "id": "dinner", "name": "Dinner", "description": "Evening meals" },
- { "id": "snack", "name": "Snack", "description": "Small bites between meals" },
- { "id": "brunch", "name": "Brunch", "description": "Late morning combination meal" }
- ]
- },
- "occasion": {
- "name": "Occasion",
- "description": "Special events and settings",
- "tags": [
- { "id": "weeknight", "name": "Weeknight", "description": "Quick meals for busy evenings" },
- { "id": "weekend", "name": "Weekend", "description": "Recipes with more time investment" },
- { "id": "holiday", "name": "Holiday", "description": "Special holiday dishes" },
- { "id": "party", "name": "Party", "description": "Entertaining and gatherings" },
- { "id": "comfort-food", "name": "Comfort Food", "description": "Cozy, satisfying dishes" },
- { "id": "family-friendly", "name": "Family-Friendly", "description": "Kid-approved recipes" },
- { "id": "date-night", "name": "Date Night", "description": "Romantic or impressive dishes" }
- ]
- },
- "cooking-method": {
- "name": "Cooking Method",
- "description": "Primary cooking technique",
- "tags": [
- { "id": "baking", "name": "Baking", "description": "Oven-baked dishes" },
- { "id": "grilling", "name": "Grilling", "description": "Outdoor or indoor grilling" },
- { "id": "slow-cooker", "name": "Slow Cooker", "description": "Crock-pot and slow cooker meals" },
- { "id": "instant-pot", "name": "Instant Pot", "description": "Pressure cooker recipes" },
- { "id": "stovetop", "name": "Stovetop", "description": "Cooked on the stove" },
- { "id": "no-cook", "name": "No-Cook", "description": "No cooking required" },
- { "id": "air-fryer", "name": "Air Fryer", "description": "Air fryer recipes" },
- { "id": "sous-vide", "name": "Sous Vide", "description": "Precision cooking method" }
- ]
- },
- "speed": {
- "name": "Speed & Convenience",
- "description": "Time and effort indicators",
- "tags": [
- { "id": "quick-meals", "name": "Quick Meals", "description": "30 minutes or less" },
- { "id": "one-pot", "name": "One-Pot", "description": "Minimal cleanup required" },
- { "id": "make-ahead", "name": "Make-Ahead", "description": "Can be prepared in advance" },
- { "id": "meal-prep", "name": "Meal Prep", "description": "Great for batch cooking" },
- { "id": "leftovers", "name": "Leftovers", "description": "Tastes great reheated" },
- { "id": "freezer-friendly", "name": "Freezer-Friendly", "description": "Can be frozen" }
- ]
- },
- "flavor-profile": {
- "name": "Flavor Profile",
- "description": "Dominant tastes and characteristics",
- "tags": [
- { "id": "spicy", "name": "Spicy", "description": "Hot and spicy flavors" },
- { "id": "sweet", "name": "Sweet", "description": "Sweet or dessert-like" },
- { "id": "savory", "name": "Savory", "description": "Rich savory flavors" },
- { "id": "tangy", "name": "Tangy", "description": "Acidic or citrus notes" },
- { "id": "rich", "name": "Rich", "description": "Indulgent and decadent" },
- { "id": "light", "name": "Light", "description": "Fresh and light dishes" },
- { "id": "smoky", "name": "Smoky", "description": "Smoky flavors" },
- { "id": "umami", "name": "Umami", "description": "Deep savory umami taste" }
- ]
- },
- "special": {
- "name": "Special Features",
- "description": "Notable characteristics",
- "tags": [
- { "id": "chocolate", "name": "Chocolate", "description": "Contains chocolate" },
- { "id": "cookies", "name": "Cookies", "description": "Cookie recipes" },
- { "id": "pasta", "name": "Pasta", "description": "Pasta dishes" },
- { "id": "soup", "name": "Soup", "description": "Soups and stews" },
- { "id": "salad", "name": "Salad", "description": "Salads and fresh dishes" },
- { "id": "sandwich", "name": "Sandwich", "description": "Sandwiches and wraps" },
- { "id": "pizza", "name": "Pizza", "description": "Pizza recipes" },
- { "id": "bbq", "name": "BBQ", "description": "Barbecue style" }
- ]
- }
- },
- "difficulty": [
- {
- "id": "easy",
- "name": "Easy",
- "description": "Beginner-friendly, simple techniques, common ingredients",
- "icon": "⭐"
- },
- {
- "id": "medium",
- "name": "Medium",
- "description": "Some cooking experience needed, multiple steps",
- "icon": "⭐⭐"
- },
- {
- "id": "hard",
- "name": "Hard",
- "description": "Advanced techniques, precise timing, specialty equipment",
- "icon": "⭐⭐⭐"
- }
- ]
-}
diff --git a/lib/recipes.ts b/lib/recipes.ts
index d3d11b6..2da737a 100644
--- a/lib/recipes.ts
+++ b/lib/recipes.ts
@@ -2,7 +2,7 @@ import fs from 'fs';
import path from 'path';
import matter from 'gray-matter';
-const recipesDirectory = path.join(process.cwd(), 'data/recipes');
+const recipesDirectory = path.join(process.cwd(), 'public/recipes');
// Cache to avoid repeated file system walks
let recipesCache: Recipe[] | null = null;
diff --git a/next.config.ts b/next.config.ts
index 363f8bd..95a6154 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -4,20 +4,6 @@ import createMDX from '@next/mdx';
const nextConfig: NextConfig = {
reactStrictMode: true,
pageExtensions: ['js', 'jsx', 'md', 'mdx', 'ts', 'tsx'],
- images: {
- remotePatterns: [
- {
- protocol: 'http',
- hostname: 'localhost',
- pathname: '/api/recipe-image**',
- },
- ],
- localPatterns: [
- {
- pathname: '/api/recipe-image**',
- },
- ],
- },
};
const withMDX = createMDX({
diff --git a/package-lock.json b/package-lock.json
index 339b81f..b6e701b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,7 +15,9 @@
"gray-matter": "^4.0.3",
"next": "^15.1.6",
"react": "^19.0.0",
- "react-dom": "^19.0.0"
+ "react-dom": "^19.0.0",
+ "react-markdown": "^10.1.0",
+ "remark-gfm": "^4.0.1"
},
"devDependencies": {
"@types/node": "^20",
@@ -4033,6 +4035,16 @@
"url": "https://opencollective.com/unified"
}
},
+ "node_modules/html-url-attributes": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz",
+ "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -4844,6 +4856,16 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/markdown-table": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz",
+ "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -4854,6 +4876,34 @@
"node": ">= 0.4"
}
},
+ "node_modules/mdast-util-find-and-replace": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz",
+ "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "escape-string-regexp": "^5.0.0",
+ "unist-util-is": "^6.0.0",
+ "unist-util-visit-parents": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
+ "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/mdast-util-from-markdown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz",
@@ -4878,6 +4928,107 @@
"url": "https://opencollective.com/unified"
}
},
+ "node_modules/mdast-util-gfm": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz",
+ "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==",
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-gfm-autolink-literal": "^2.0.0",
+ "mdast-util-gfm-footnote": "^2.0.0",
+ "mdast-util-gfm-strikethrough": "^2.0.0",
+ "mdast-util-gfm-table": "^2.0.0",
+ "mdast-util-gfm-task-list-item": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-autolink-literal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz",
+ "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "ccount": "^2.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-find-and-replace": "^3.0.0",
+ "micromark-util-character": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-footnote": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz",
+ "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.1.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-strikethrough": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz",
+ "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-table": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz",
+ "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "markdown-table": "^3.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-task-list-item": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz",
+ "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/mdast-util-mdx": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz",
@@ -5103,6 +5254,127 @@
"micromark-util-types": "^2.0.0"
}
},
+ "node_modules/micromark-extension-gfm": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz",
+ "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==",
+ "license": "MIT",
+ "dependencies": {
+ "micromark-extension-gfm-autolink-literal": "^2.0.0",
+ "micromark-extension-gfm-footnote": "^2.0.0",
+ "micromark-extension-gfm-strikethrough": "^2.0.0",
+ "micromark-extension-gfm-table": "^2.0.0",
+ "micromark-extension-gfm-tagfilter": "^2.0.0",
+ "micromark-extension-gfm-task-list-item": "^2.0.0",
+ "micromark-util-combine-extensions": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-autolink-literal": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz",
+ "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==",
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-footnote": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz",
+ "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-core-commonmark": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-strikethrough": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz",
+ "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-table": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz",
+ "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-tagfilter": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz",
+ "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==",
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-task-list-item": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz",
+ "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/micromark-extension-mdx-expression": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.1.tgz",
@@ -6415,6 +6687,33 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/react-markdown": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz",
+ "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "hast-util-to-jsx-runtime": "^2.0.0",
+ "html-url-attributes": "^3.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "remark-parse": "^11.0.0",
+ "remark-rehype": "^11.0.0",
+ "unified": "^11.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ },
+ "peerDependencies": {
+ "@types/react": ">=18",
+ "react": ">=18"
+ }
+ },
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@@ -6564,6 +6863,24 @@
"url": "https://opencollective.com/unified"
}
},
+ "node_modules/remark-gfm": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz",
+ "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-gfm": "^3.0.0",
+ "micromark-extension-gfm": "^3.0.0",
+ "remark-parse": "^11.0.0",
+ "remark-stringify": "^11.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/remark-mdx": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.1.1.tgz",
@@ -6611,6 +6928,21 @@
"url": "https://opencollective.com/unified"
}
},
+ "node_modules/remark-stringify": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz",
+ "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/resolve": {
"version": "1.22.11",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
diff --git a/package.json b/package.json
index d35a9b2..a09b372 100644
--- a/package.json
+++ b/package.json
@@ -16,7 +16,9 @@
"gray-matter": "^4.0.3",
"next": "^15.1.6",
"react": "^19.0.0",
- "react-dom": "^19.0.0"
+ "react-dom": "^19.0.0",
+ "react-markdown": "^10.1.0",
+ "remark-gfm": "^4.0.1"
},
"devDependencies": {
"@types/node": "^20",