Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Cookbook Generator</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| .fade-in { | |
| animation: fadeIn 0.5s ease-in-out; | |
| } | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: translateY(10px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| .recipe-card { | |
| transition: all 0.3s ease; | |
| } | |
| .recipe-card:hover { | |
| transform: translateY(-5px); | |
| box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1); | |
| } | |
| .progress-step { | |
| position: relative; | |
| } | |
| .progress-step:not(:last-child):after { | |
| content: ''; | |
| position: absolute; | |
| top: 24px; | |
| left: 50%; | |
| height: 40px; | |
| width: 2px; | |
| background-color: #e5e7eb; | |
| transform: translateX(-50%); | |
| } | |
| .progress-step.active:not(:last-child):after { | |
| background-color: #3b82f6; | |
| } | |
| .markdown-content h2 { | |
| font-size: 1.5rem; | |
| font-weight: bold; | |
| margin-top: 1.5rem; | |
| margin-bottom: 1rem; | |
| color: #1f2937; | |
| } | |
| .markdown-content h3 { | |
| font-size: 1.25rem; | |
| font-weight: bold; | |
| margin-top: 1.25rem; | |
| margin-bottom: 0.75rem; | |
| color: #374151; | |
| } | |
| .markdown-content ul { | |
| list-style-type: disc; | |
| padding-left: 1.5rem; | |
| margin-bottom: 1rem; | |
| } | |
| .markdown-content ol { | |
| list-style-type: decimal; | |
| padding-left: 1.5rem; | |
| margin-bottom: 1rem; | |
| } | |
| .markdown-content p { | |
| margin-bottom: 1rem; | |
| line-height: 1.6; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-50 min-h-screen"> | |
| <div class="container mx-auto px-4 py-8 max-w-6xl"> | |
| <!-- Header --> | |
| <header class="text-center mb-12"> | |
| <h1 class="text-4xl font-bold text-gray-800 mb-2">Cookbook Generator</h1> | |
| <p class="text-lg text-gray-600">Create professional cookbooks with AI assistance</p> | |
| </header> | |
| <!-- Progress Steps --> | |
| <div class="flex justify-between mb-12 relative"> | |
| <div class="progress-step text-center w-1/6 active" id="step1-indicator"> | |
| <div class="w-12 h-12 mx-auto rounded-full bg-blue-500 text-white flex items-center justify-center font-bold mb-2">1</div> | |
| <span class="text-sm font-medium text-gray-700">Book Details</span> | |
| </div> | |
| <div class="progress-step text-center w-1/6" id="step2-indicator"> | |
| <div class="w-12 h-12 mx-auto rounded-full bg-gray-200 text-gray-600 flex items-center justify-center font-bold mb-2">2</div> | |
| <span class="text-sm font-medium text-gray-500">Table of Contents</span> | |
| </div> | |
| <div class="progress-step text-center w-1/6" id="step3-indicator"> | |
| <div class="w-12 h-12 mx-auto rounded-full bg-gray-200 text-gray-600 flex items-center justify-center font-bold mb-2">3</div> | |
| <span class="text-sm font-medium text-gray-500">Recipe Format</span> | |
| </div> | |
| <div class="progress-step text-center w-1/6" id="step4-indicator"> | |
| <div class="w-12 h-12 mx-auto rounded-full bg-gray-200 text-gray-600 flex items-center justify-center font-bold mb-2">4</div> | |
| <span class="text-sm font-medium text-gray-500">Generate Recipes</span> | |
| </div> | |
| <div class="progress-step text-center w-1/6" id="step5-indicator"> | |
| <div class="w-12 h-12 mx-auto rounded-full bg-gray-200 text-gray-600 flex items-center justify-center font-bold mb-2">5</div> | |
| <span class="text-sm font-medium text-gray-500">Front/Back Matter</span> | |
| </div> | |
| <div class="progress-step text-center w-1/6" id="step6-indicator"> | |
| <div class="w-12 h-12 mx-auto rounded-full bg-gray-200 text-gray-600 flex items-center justify-center font-bold mb-2">6</div> | |
| <span class="text-sm font-medium text-gray-500">Export</span> | |
| </div> | |
| </div> | |
| <!-- Main Content Area --> | |
| <div class="bg-white rounded-xl shadow-md p-6 mb-8"> | |
| <!-- Step 1: Book Details --> | |
| <div id="step1" class="step-content fade-in"> | |
| <h2 class="text-2xl font-bold text-gray-800 mb-6">1. Enter Cookbook Details</h2> | |
| <div class="mb-6"> | |
| <label for="book-title" class="block text-sm font-medium text-gray-700 mb-1">Cookbook Title</label> | |
| <input type="text" id="book-title" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500" placeholder="e.g., Mediterranean Cooking Made Easy"> | |
| </div> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6"> | |
| <div> | |
| <label for="recipe-count" class="block text-sm font-medium text-gray-700 mb-1">Total Number of Recipes</label> | |
| <input type="number" id="recipe-count" min="1" max="200" value="50" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500"> | |
| </div> | |
| <div> | |
| <label for="chapter-count" class="block text-sm font-medium text-gray-700 mb-1">Number of Chapters</label> | |
| <input type="number" id="chapter-count" min="1" max="20" value="5" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500"> | |
| </div> | |
| </div> | |
| <div class="flex justify-end"> | |
| <button id="next-to-step2" class="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors">Next: Generate TOC <i class="fas fa-arrow-right ml-2"></i></button> | |
| </div> | |
| </div> | |
| <!-- Step 2: Table of Contents --> | |
| <div id="step2" class="step-content hidden"> | |
| <h2 class="text-2xl font-bold text-gray-800 mb-6">2. Generate Table of Contents</h2> | |
| <div class="mb-6 bg-blue-50 border border-blue-200 rounded-lg p-4"> | |
| <p class="text-blue-800">We'll use GPT-4 to create a structured table of contents based on your recipe count and chapter count.</p> | |
| </div> | |
| <div class="mb-6"> | |
| <label for="book-theme" class="block text-sm font-medium text-gray-700 mb-1">Optional: Cookbook Theme/Cuisine</label> | |
| <input type="text" id="book-theme" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500" placeholder="e.g., Italian, Vegan, Quick Meals"> | |
| </div> | |
| <div class="flex justify-between mb-6"> | |
| <button id="back-to-step1" class="px-6 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors"><i class="fas fa-arrow-left mr-2"></i> Back</button> | |
| <button id="generate-toc" class="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors flex items-center"> | |
| <span id="generate-toc-text">Generate TOC</span> | |
| <span id="generate-toc-spinner" class="hidden ml-2"> | |
| <i class="fas fa-spinner fa-spin"></i> | |
| </span> | |
| </button> | |
| </div> | |
| <div id="toc-results" class="hidden"> | |
| <h3 class="text-xl font-semibold text-gray-700 mb-4">Generated Table of Contents</h3> | |
| <div id="toc-preview" class="border border-gray-200 rounded-lg p-4 mb-6 bg-gray-50"></div> | |
| <div class="flex justify-between"> | |
| <button id="regenerate-toc" class="px-6 py-2 bg-yellow-500 text-white rounded-lg hover:bg-yellow-600 transition-colors flex items-center"> | |
| <i class="fas fa-sync-alt mr-2"></i> Regenerate | |
| </button> | |
| <button id="next-to-step3" class="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors">Next: Recipe Format <i class="fas fa-arrow-right ml-2"></i></button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Step 3: Recipe Format --> | |
| <div id="step3" class="step-content hidden"> | |
| <h2 class="text-2xl font-bold text-gray-800 mb-6">3. Define Recipe Format</h2> | |
| <div class="mb-6 bg-blue-50 border border-blue-200 rounded-lg p-4"> | |
| <p class="text-blue-800">Define the structure for each recipe in your cookbook. You can use our default format or customize it.</p> | |
| </div> | |
| <div class="mb-6"> | |
| <div class="flex items-center mb-4"> | |
| <input type="radio" id="default-format" name="recipe-format" value="default" checked class="h-4 w-4 text-blue-600 focus:ring-blue-500"> | |
| <label for="default-format" class="ml-2 block text-sm font-medium text-gray-700">Use Default Recipe Format</label> | |
| </div> | |
| <div class="flex items-center mb-4"> | |
| <input type="radio" id="custom-format" name="recipe-format" value="custom" class="h-4 w-4 text-blue-600 focus:ring-blue-500"> | |
| <label for="custom-format" class="ml-2 block text-sm font-medium text-gray-700">Customize Recipe Format</label> | |
| </div> | |
| <div id="custom-format-container" class="hidden mt-4"> | |
| <label for="recipe-template" class="block text-sm font-medium text-gray-700 mb-1">Recipe Template (Markdown format)</label> | |
| <textarea id="recipe-template" rows="12" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500 font-mono text-sm"># {Recipe Name} | |
| ## Description | |
| {2-3 sentence description of the recipe, its origins, and why readers will love it} | |
| ## Ingredients | |
| - {Ingredient 1} - {Quantity} | |
| - {Ingredient 2} - {Quantity} | |
| - ... (list all ingredients) | |
| ## Instructions | |
| 1. {Step 1} | |
| 2. {Step 2} | |
| 3. ... (detailed steps) | |
| ## Tips & Variations | |
| - {Tip 1} | |
| - {Tip 2} | |
| - {Variation suggestion} | |
| ## Nutrition Information (per serving) | |
| - Calories: {number} | |
| - Protein: {number}g | |
| - Carbs: {number}g | |
| - Fat: {number}g</textarea> | |
| <p class="text-sm text-gray-500 mt-1">Use placeholders like {Recipe Name} that will be filled by the AI.</p> | |
| </div> | |
| </div> | |
| <div class="flex justify-between"> | |
| <button id="back-to-step2" class="px-6 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors"><i class="fas fa-arrow-left mr-2"></i> Back</button> | |
| <button id="next-to-step4" class="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors">Next: Generate Recipes <i class="fas fa-arrow-right ml-2"></i></button> | |
| </div> | |
| </div> | |
| <!-- Step 4: Generate Recipes --> | |
| <div id="step4" class="step-content hidden"> | |
| <h2 class="text-2xl font-bold text-gray-800 mb-6">4. Generate Recipes</h2> | |
| <div class="mb-6 bg-blue-50 border border-blue-200 rounded-lg p-4"> | |
| <p class="text-blue-800">We'll generate recipes in batches of 10. You can review and regenerate individual recipes as needed.</p> | |
| </div> | |
| <div class="mb-6"> | |
| <label for="recipe-batch-select" class="block text-sm font-medium text-gray-700 mb-1">Select Chapter to Generate</label> | |
| <select id="recipe-batch-select" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500"> | |
| <!-- Filled dynamically --> | |
| </select> | |
| </div> | |
| <div class="flex justify-between mb-6"> | |
| <button id="back-to-step3" class="px-6 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors"><i class="fas fa-arrow-left mr-2"></i> Back</button> | |
| <button id="generate-recipes" class="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors flex items-center"> | |
| <span id="generate-recipes-text">Generate Recipes</span> | |
| <span id="generate-recipes-spinner" class="hidden ml-2"> | |
| <i class="fas fa-spinner fa-spin"></i> | |
| </span> | |
| </button> | |
| </div> | |
| <div id="recipe-results" class="hidden"> | |
| <h3 class="text-xl font-semibold text-gray-700 mb-4">Generated Recipes</h3> | |
| <div id="recipe-list" class="space-y-6"></div> | |
| <div class="flex justify-between mt-6"> | |
| <button id="regenerate-recipes" class="px-6 py-2 bg-yellow-500 text-white rounded-lg hover:bg-yellow-600 transition-colors flex items-center"> | |
| <i class="fas fa-sync-alt mr-2"></i> Regenerate All | |
| </button> | |
| <button id="next-to-step5" class="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors">Next: Front/Back Matter <i class="fas fa-arrow-right ml-2"></i></button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Step 5: Front & Back Matter --> | |
| <div id="step5" class="step-content hidden"> | |
| <h2 class="text-2xl font-bold text-gray-800 mb-6">5. Add Front & Back Matter</h2> | |
| <div class="mb-6 bg-blue-50 border border-blue-200 rounded-lg p-4"> | |
| <p class="text-blue-800">Generate additional book sections like introduction, pantry essentials, and conclusion.</p> | |
| </div> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6"> | |
| <div class="border border-gray-200 rounded-lg p-4"> | |
| <h3 class="text-lg font-semibold text-gray-700 mb-3">Front Matter</h3> | |
| <div class="space-y-3"> | |
| <div class="flex items-center"> | |
| <input type="checkbox" id="generate-intro" checked class="h-4 w-4 text-blue-600 focus:ring-blue-500"> | |
| <label for="generate-intro" class="ml-2 block text-sm font-medium text-gray-700">Introduction (~1000 words)</label> | |
| </div> | |
| <div class="flex items-center"> | |
| <input type="checkbox" id="generate-pantry" checked class="h-4 w-4 text-blue-600 focus:ring-blue-500"> | |
| <label for="generate-pantry" class="ml-2 block text-sm font-medium text-gray-700">Pantry Essentials (~1000 words)</label> | |
| </div> | |
| <div class="flex items-center"> | |
| <input type="checkbox" id="generate-tips" checked class="h-4 w-4 text-blue-600 focus:ring-blue-500"> | |
| <label for="generate-tips" class="ml-2 block text-sm font-medium text-gray-700">Cooking Tips (~800 words)</label> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="border border-gray-200 rounded-lg p-4"> | |
| <h3 class="text-lg font-semibold text-gray-700 mb-3">Back Matter</h3> | |
| <div class="space-y-3"> | |
| <div class="flex items-center"> | |
| <input type="checkbox" id="generate-conclusion" checked class="h-4 w-4 text-blue-600 focus:ring-blue-500"> | |
| <label for="generate-conclusion" class="ml-2 block text-sm font-medium text-gray-700">Conclusion (~800 words)</label> | |
| </div> | |
| <div class="flex items-center"> | |
| <input type="checkbox" id="generate-index" checked class="h-4 w-4 text-blue-600 focus:ring-blue-500"> | |
| <label for="generate-index" class="ml-2 block text-sm font-medium text-gray-700">Recipe Index</label> | |
| </div> | |
| <div class="flex items-center"> | |
| <input type="checkbox" id="generate-conversion" checked class="h-4 w-4 text-blue-600 focus:ring-blue-500"> | |
| <label for="generate-conversion" class="ml-2 block text-sm font-medium text-gray-700">Measurement Conversions</label> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="flex justify-between mb-6"> | |
| <button id="back-to-step4" class="px-6 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors"><i class="fas fa-arrow-left mr-2"></i> Back</button> | |
| <button id="generate-matter" class="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors flex items-center"> | |
| <span id="generate-matter-text">Generate Sections</span> | |
| <span id="generate-matter-spinner" class="hidden ml-2"> | |
| <i class="fas fa-spinner fa-spin"></i> | |
| </span> | |
| </button> | |
| </div> | |
| <div id="matter-results" class="hidden"> | |
| <h3 class="text-xl font-semibold text-gray-700 mb-4">Generated Sections</h3> | |
| <div id="matter-preview" class="space-y-6"></div> | |
| <div class="flex justify-between mt-6"> | |
| <button id="regenerate-matter" class="px-6 py-2 bg-yellow-500 text-white rounded-lg hover:bg-yellow-600 transition-colors flex items-center"> | |
| <i class="fas fa-sync-alt mr-2"></i> Regenerate All | |
| </button> | |
| <button id="next-to-step6" class="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors">Next: Preview & Export <i class="fas fa-arrow-right ml-2"></i></button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Step 6: Export --> | |
| <div id="step6" class="step-content hidden"> | |
| <h2 class="text-2xl font-bold text-gray-800 mb-6">6. Preview & Export Cookbook</h2> | |
| <div class="mb-6 bg-blue-50 border border-blue-200 rounded-lg p-4"> | |
| <p class="text-blue-800">Preview your complete cookbook and export it in your preferred format.</p> | |
| </div> | |
| <div class="mb-6"> | |
| <label for="export-options" class="block text-sm font-medium text-gray-700 mb-2">Export Options</label> | |
| <div class="grid grid-cols-1 md:grid-cols-3 gap-4"> | |
| <div class="border border-gray-200 rounded-lg p-4 hover:border-blue-300 hover:bg-blue-50 transition-colors cursor-pointer export-option" data-format="full-pdf"> | |
| <div class="flex items-center"> | |
| <div class="p-2 bg-red-100 rounded-full mr-3"> | |
| <i class="fas fa-file-pdf text-red-500"></i> | |
| </div> | |
| <div> | |
| <h4 class="font-medium text-gray-800">Full Cookbook (PDF)</h4> | |
| <p class="text-sm text-gray-500">Single PDF file</p> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="border border-gray-200 rounded-lg p-4 hover:border-blue-300 hover:bg-blue-50 transition-colors cursor-pointer export-option" data-format="full-docx"> | |
| <div class="flex items-center"> | |
| <div class="p-2 bg-blue-100 rounded-full mr-3"> | |
| <i class="fas fa-file-word text-blue-500"></i> | |
| </div> | |
| <div> | |
| <h4 class="font-medium text-gray-800">Full Cookbook (Word)</h4> | |
| <p class="text-sm text-gray-500">Single .docx file</p> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="border border-gray-200 rounded-lg p-4 hover:border-blue-300 hover:bg-blue-50 transition-colors cursor-pointer export-option" data-format="chapter-docx"> | |
| <div class="flex items-center"> | |
| <div class="p-2 bg-green-100 rounded-full mr-3"> | |
| <i class="fas fa-folder text-green-500"></i> | |
| </div> | |
| <div> | |
| <h4 class="font-medium text-gray-800">By Chapter (Word)</h4> | |
| <p class="text-sm text-gray-500">ZIP with separate files</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="mb-6"> | |
| <label for="book-settings" class="block text-sm font-medium text-gray-700 mb-2">Book Settings</label> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| <div> | |
| <label for="book-size" class="block text-sm font-medium text-gray-700 mb-1">Book Size</label> | |
| <select id="book-size" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500"> | |
| <option value="6x9">6" x 9" (Standard)</option> | |
| <option value="5.5x8.5">5.5" x 8.5"</option> | |
| <option value="8x10">8" x 10"</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label for="font-family" class="block text-sm font-medium text-gray-700 mb-1">Font</label> | |
| <select id="font-family" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500"> | |
| <option value="times">Times New Roman</option> | |
| <option value="arial">Arial</option> | |
| <option value="georgia">Georgia</option> | |
| <option value="garamond">Garamond</option> | |
| </select> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="flex justify-between"> | |
| <button id="back-to-step5" class="px-6 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors"><i class="fas fa-arrow-left mr-2"></i> Back</button> | |
| <button id="export-book" class="px-6 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors flex items-center"> | |
| <span id="export-book-text">Export Cookbook</span> | |
| <span id="export-book-spinner" class="hidden ml-2"> | |
| <i class="fas fa-spinner fa-spin"></i> | |
| </span> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Preview Modal --> | |
| <div id="preview-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> | |
| <div class="bg-white rounded-xl shadow-xl w-full max-w-4xl max-h-[90vh] flex flex-col"> | |
| <div class="flex justify-between items-center border-b border-gray-200 px-6 py-4"> | |
| <h3 class="text-xl font-bold text-gray-800">Cookbook Preview</h3> | |
| <button id="close-preview" class="text-gray-500 hover:text-gray-700"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <div class="p-6 overflow-y-auto flex-grow"> | |
| <div id="full-preview-content" class="prose max-w-none markdown-content"></div> | |
| </div> | |
| <div class="border-t border-gray-200 px-6 py-4 flex justify-end"> | |
| <button id="export-from-preview" class="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"> | |
| Export Now | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Sample data structure to store the cookbook | |
| const cookbook = { | |
| title: '', | |
| recipeCount: 0, | |
| chapterCount: 0, | |
| theme: '', | |
| toc: [], | |
| recipeFormat: '', | |
| recipes: {}, | |
| frontMatter: {}, | |
| backMatter: {} | |
| }; | |
| // DOM Elements | |
| const stepContents = document.querySelectorAll('.step-content'); | |
| const stepIndicators = document.querySelectorAll('.progress-step'); | |
| // Navigation functions | |
| function showStep(stepNumber) { | |
| // Hide all steps | |
| stepContents.forEach(step => step.classList.add('hidden')); | |
| stepContents.forEach(step => step.classList.remove('fade-in')); | |
| // Show current step | |
| const currentStep = document.getElementById(`step${stepNumber}`); | |
| currentStep.classList.remove('hidden'); | |
| setTimeout(() => { | |
| currentStep.classList.add('fade-in'); | |
| }, 10); | |
| // Update progress indicators | |
| stepIndicators.forEach((indicator, index) => { | |
| if (index < stepNumber) { | |
| indicator.classList.add('active'); | |
| indicator.querySelector('div').classList.remove('bg-gray-200', 'text-gray-600'); | |
| indicator.querySelector('div').classList.add('bg-blue-500', 'text-white'); | |
| indicator.querySelector('span').classList.remove('text-gray-500'); | |
| indicator.querySelector('span').classList.add('text-gray-700'); | |
| } else { | |
| indicator.classList.remove('active'); | |
| indicator.querySelector('div').classList.add('bg-gray-200', 'text-gray-600'); | |
| indicator.querySelector('div').classList.remove('bg-blue-500', 'text-white'); | |
| indicator.querySelector('span').classList.add('text-gray-500'); | |
| indicator.querySelector('span').classList.remove('text-gray-700'); | |
| } | |
| }); | |
| // Scroll to top | |
| window.scrollTo({ top: 0, behavior: 'smooth' }); | |
| } | |
| // Step 1: Book Details | |
| document.getElementById('next-to-step2').addEventListener('click', () => { | |
| const title = document.getElementById('book-title').value.trim(); | |
| const recipeCount = parseInt(document.getElementById('recipe-count').value); | |
| const chapterCount = parseInt(document.getElementById('chapter-count').value); | |
| if (!title) { | |
| alert('Please enter a cookbook title'); | |
| return; | |
| } | |
| if (recipeCount < 1 || chapterCount < 1) { | |
| alert('Recipe count and chapter count must be at least 1'); | |
| return; | |
| } | |
| cookbook.title = title; | |
| cookbook.recipeCount = recipeCount; | |
| cookbook.chapterCount = chapterCount; | |
| showStep(2); | |
| }); | |
| // Step 2: Table of Contents | |
| document.getElementById('back-to-step1').addEventListener('click', () => showStep(1)); | |
| document.getElementById('generate-toc').addEventListener('click', () => { | |
| const theme = document.getElementById('book-theme').value.trim(); | |
| cookbook.theme = theme; | |
| // Show loading state | |
| document.getElementById('generate-toc-text').classList.add('hidden'); | |
| document.getElementById('generate-toc-spinner').classList.remove('hidden'); | |
| // Simulate API call with timeout | |
| setTimeout(() => { | |
| // Generate sample TOC | |
| cookbook.toc = generateSampleTOC(cookbook.chapterCount, cookbook.recipeCount, cookbook.theme); | |
| // Display TOC | |
| displayTOCPreview(); | |
| // Hide loading state | |
| document.getElementById('generate-toc-text').classList.remove('hidden'); | |
| document.getElementById('generate-toc-spinner').classList.add('hidden'); | |
| // Show results | |
| document.getElementById('toc-results').classList.remove('hidden'); | |
| }, 1500); | |
| }); | |
| document.getElementById('regenerate-toc').addEventListener('click', () => { | |
| // Show loading state | |
| document.getElementById('generate-toc-text').classList.add('hidden'); | |
| document.getElementById('generate-toc-spinner').classList.remove('hidden'); | |
| // Simulate API call with timeout | |
| setTimeout(() => { | |
| // Generate new sample TOC | |
| cookbook.toc = generateSampleTOC(cookbook.chapterCount, cookbook.recipeCount, cookbook.theme); | |
| // Display TOC | |
| displayTOCPreview(); | |
| // Hide loading state | |
| document.getElementById('generate-toc-text').classList.remove('hidden'); | |
| document.getElementById('generate-toc-spinner').classList.add('hidden'); | |
| }, 1500); | |
| }); | |
| document.getElementById('next-to-step3').addEventListener('click', () => showStep(3)); | |
| function generateSampleTOC(chapterCount, recipeCount, theme = '') { | |
| const chapters = []; | |
| const recipesPerChapter = Math.floor(recipeCount / chapterCount); | |
| const remainingRecipes = recipeCount % chapterCount; | |
| const chapterThemes = getChapterThemes(chapterCount, theme); | |
| for (let i = 0; i < chapterCount; i++) { | |
| const chapterRecipes = []; | |
| const currentRecipeCount = recipesPerChapter + (i < remainingRecipes ? 1 : 0); | |
| for (let j = 0; j < currentRecipeCount; j++) { | |
| chapterRecipes.push(`Recipe ${j + 1} - ${generateRecipeName(chapterThemes[i])}`); | |
| } | |
| chapters.push({ | |
| title: chapterThemes[i], | |
| recipes: chapterRecipes | |
| }); | |
| } | |
| return chapters; | |
| } | |
| function getChapterThemes(count, mainTheme = '') { | |
| const themes = { | |
| 'Italian': ['Antipasti', 'Primi Piatti', 'Secondi Piatti', 'Contorni', 'Dolci'], | |
| 'Mexican': ['Appetizers', 'Tacos & Enchiladas', 'Soups & Stews', 'Main Dishes', 'Desserts'], | |
| 'Asian': ['Dim Sum & Appetizers', 'Noodle Dishes', 'Rice Dishes', 'Curries', 'Desserts'], | |
| 'Mediterranean': ['Meze & Appetizers', 'Salads', 'Main Courses', 'Seafood', 'Desserts'], | |
| 'Vegan': ['Breakfast', 'Soups & Salads', 'Main Dishes', 'Snacks', 'Desserts'], | |
| 'default': ['Breakfast', 'Appetizers', 'Main Dishes', 'Side Dishes', 'Desserts'] | |
| }; | |
| if (mainTheme && themes[mainTheme]) { | |
| return themes[mainTheme].slice(0, count); | |
| } | |
| // Extend default themes if more chapters needed | |
| if (count > 5) { | |
| const extended = [...themes.default]; | |
| for (let i = 5; i < count; i++) { | |
| extended.push(`Chapter ${i + 1}`); | |
| } | |
| return extended.slice(0, count); | |
| } | |
| return themes.default.slice(0, count); | |
| } | |
| function generateRecipeName(chapterTheme) { | |
| const recipeNames = { | |
| 'Breakfast': ['Fluffy Pancakes', 'Avocado Toast', 'Berry Smoothie Bowl', 'Vegetable Omelette'], | |
| 'Appetizers': ['Bruschetta', 'Stuffed Mushrooms', 'Spring Rolls', 'Deviled Eggs'], | |
| 'Main Dishes': ['Chicken Parmesan', 'Beef Stir Fry', 'Vegetable Lasagna', 'Grilled Salmon'], | |
| 'Side Dishes': ['Garlic Mashed Potatoes', 'Roasted Vegetables', 'Creamed Spinach', 'Herb Rice'], | |
| 'Desserts': ['Chocolate Cake', 'Apple Pie', 'Tiramisu', 'Cheesecake'] | |
| }; | |
| for (const key in recipeNames) { | |
| if (chapterTheme.includes(key)) { | |
| return recipeNames[key][Math.floor(Math.random() * recipeNames[key].length)]; | |
| } | |
| } | |
| return 'Delicious Recipe'; | |
| } | |
| function displayTOCPreview() { | |
| const tocPreview = document.getElementById('toc-preview'); | |
| tocPreview.innerHTML = ''; | |
| cookbook.toc.forEach((chapter, index) => { | |
| const chapterDiv = document.createElement('div'); | |
| chapterDiv.className = 'mb-4'; | |
| const chapterTitle = document.createElement('h4'); | |
| chapterTitle.className = 'font-bold text-lg text-gray-800 mb-2'; | |
| chapterTitle.textContent = `Chapter ${index + 1}: ${chapter.title}`; | |
| const recipeList = document.createElement('ul'); | |
| recipeList.className = 'list-disc pl-5 space-y-1'; | |
| chapter.recipes.forEach(recipe => { | |
| const recipeItem = document.createElement('li'); | |
| recipeItem.className = 'text-gray-700'; | |
| recipeItem.textContent = recipe; | |
| recipeList.appendChild(recipeItem); | |
| }); | |
| chapterDiv.appendChild(chapterTitle); | |
| chapterDiv.appendChild(recipeList); | |
| tocPreview.appendChild(chapterDiv); | |
| }); | |
| } | |
| // Step 3: Recipe Format | |
| document.getElementById('back-to-step2').addEventListener('click', () => showStep(2)); | |
| document.getElementById('custom-format').addEventListener('change', function() { | |
| if (this.checked) { | |
| document.getElementById('custom-format-container').classList.remove('hidden'); | |
| } | |
| }); | |
| document.getElementById('default-format').addEventListener('change', function() { | |
| if (this.checked) { | |
| document.getElementById('custom-format-container').classList.add('hidden'); | |
| } | |
| }); | |
| document.getElementById('next-to-step4').addEventListener('click', () => { | |
| const useDefaultFormat = document.getElementById('default-format').checked; | |
| if (useDefaultFormat) { | |
| cookbook.recipeFormat = `# {Recipe Name} | |
| ## Description | |
| {2-3 sentence description of the recipe, its origins, and why readers will love it} | |
| ## Ingredients | |
| - {Ingredient 1} - {Quantity} | |
| - {Ingredient 2} - {Quantity} | |
| - ... (list all ingredients) | |
| ## Instructions | |
| 1. {Step 1} | |
| 2. {Step 2} | |
| 3. ... (detailed steps) | |
| ## Tips & Variations | |
| - {Tip 1} | |
| - {Tip 2} | |
| - {Variation suggestion} | |
| ## Nutrition Information (per serving) | |
| - Calories: {number} | |
| - Protein: {number}g | |
| - Carbs: {number}g | |
| - Fat: {number}g`; | |
| } else { | |
| cookbook.recipeFormat = document.getElementById('recipe-template').value; | |
| } | |
| // Initialize recipe storage | |
| cookbook.recipes = {}; | |
| cookbook.toc.forEach(chapter => { | |
| cookbook.recipes[chapter.title] = Array(chapter.recipes.length).fill(null); | |
| }); | |
| // Populate chapter select for recipe generation | |
| const select = document.getElementById('recipe-batch-select'); | |
| select.innerHTML = ''; | |
| cookbook.toc.forEach((chapter, index) => { | |
| const option = document.createElement('option'); | |
| option.value = index; | |
| option.textContent = `Chapter ${index + 1}: ${chapter.title} (${chapter.recipes.length} recipes)`; | |
| select.appendChild(option); | |
| }); | |
| showStep(4); | |
| }); | |
| // Step 4: Generate Recipes | |
| document.getElementById('back-to-step3').addEventListener('click', () => showStep(3)); | |
| document.getElementById('generate-recipes').addEventListener('click', () => { | |
| const chapterIndex = parseInt(document.getElementById('recipe-batch-select').value); | |
| const chapter = cookbook.toc[chapterIndex]; | |
| // Show loading state | |
| document.getElementById('generate-recipes-text').classList.add('hidden'); | |
| document.getElementById('generate-recipes-spinner').classList.remove('hidden'); | |
| // Simulate API call with timeout | |
| setTimeout(() => { | |
| // Generate sample recipes for all recipes in the chapter | |
| for (let i = 0; i < chapter.recipes.length; i++) { | |
| if (!cookbook.recipes[chapter.title][i]) { | |
| cookbook.recipes[chapter.title][i] = generateSampleRecipe(chapter.recipes[i]); | |
| } | |
| } | |
| // Display recipes | |
| displayRecipes(chapter.title); | |
| // Hide loading state | |
| document.getElementById('generate-recipes-text').classList.remove('hidden'); | |
| document.getElementById('generate-recipes-spinner').classList.add('hidden'); | |
| // Show results | |
| document.getElementById('recipe-results').classList.remove('hidden'); | |
| }, 2000); | |
| }); | |
| document.getElementById('regenerate-recipes').addEventListener('click', () => { | |
| const chapterIndex = parseInt(document.getElementById('recipe-batch-select').value); | |
| const chapter = cookbook.toc[chapterIndex]; | |
| // Show loading state | |
| document.getElementById('generate-recipes-text').classList.add('hidden'); | |
| document.getElementById('generate-recipes-spinner').classList.remove('hidden'); | |
| // Simulate API call with timeout | |
| setTimeout(() => { | |
| // Generate new sample recipes for all recipes in the chapter | |
| for (let i = 0; i < chapter.recipes.length; i++) { | |
| cookbook.recipes[chapter.title][i] = generateSampleRecipe(chapter.recipes[i]); | |
| } | |
| // Display recipes | |
| displayRecipes(chapter.title); | |
| // Hide loading state | |
| document.getElementById('generate-recipes-text').classList.remove('hidden'); | |
| document.getElementById('generate-recipes-spinner').classList.add('hidden'); | |
| }, 2000); | |
| }); | |
| document.getElementById('next-to-step5').addEventListener('click', () => showStep(5)); | |
| function generateSampleRecipe(recipeName) { | |
| const recipeNameOnly = recipeName.split(' - ')[1] || recipeName; | |
| const steps = [ | |
| "Prepare all ingredients as listed", | |
| "Preheat oven to 375°F (190°C) if baking is required", | |
| "Combine dry ingredients in a large mixing bowl", | |
| "Whisk together wet ingredients in a separate bowl", | |
| "Gradually add wet ingredients to dry ingredients while stirring", | |
| "Mix until just combined (do not overmix)", | |
| "Pour batter into prepared baking dish if needed", | |
| "Bake for 25-30 minutes until golden brown", | |
| "Let cool for 5-10 minutes before serving", | |
| "Garnish with fresh herbs or toppings as desired" | |
| ]; | |
| // Randomly select 4-8 steps for variety | |
| const selectedSteps = []; | |
| const numSteps = 4 + Math.floor(Math.random() * 4); | |
| const stepIndices = Array.from({length: steps.length}, (_, i) => i) | |
| .sort(() => Math.random() - 0.5) | |
| .slice(0, numSteps) | |
| .sort((a, b) => a - b); | |
| stepIndices.forEach((index, i) => { | |
| selectedSteps.push(`${i + 1}. ${steps[index]}`); | |
| }); | |
| return `# ${recipeNameOnly} | |
| ## Description | |
| ${recipeNameOnly} is a delicious dish that combines fresh ingredients with simple techniques to create a meal that's both satisfying and flavorful. This recipe has been passed down through generations and is perfect for weeknight dinners or special occasions. | |
| ## Ingredients | |
| - 2 cups all-purpose flour | |
| - 1 teaspoon salt | |
| - 1 tablespoon sugar | |
| - 1 packet active dry yeast | |
| - 3/4 cup warm water | |
| - 2 tablespoons olive oil | |
| - 1 cup tomato sauce | |
| - 2 cups shredded mozzarella cheese | |
| - Assorted toppings (pepperoni, mushrooms, bell peppers, etc.) | |
| ## Instructions | |
| ${selectedSteps.join('\n')} | |
| ## Tips & Variations | |
| - For a crispier crust, pre-bake the dough for 5 minutes before adding toppings | |
| - Try using whole wheat flour for a healthier alternative | |
| - Add fresh basil after baking for extra flavor | |
| ## Nutrition Information (per serving) | |
| - Calories: 350 | |
| - Protein: 15g | |
| - Carbs: 45g | |
| - Fat: 12g`; | |
| } | |
| function displayRecipes(chapterTitle) { | |
| const recipeList = document.getElementById('recipe-list'); | |
| recipeList.innerHTML = ''; | |
| cookbook.recipes[chapterTitle].forEach((recipe, index) => { | |
| if (!recipe) return; | |
| const recipeCard = document.createElement('div'); | |
| recipeCard.className = 'recipe-card bg-white border border-gray-200 rounded-lg p-6 shadow-sm'; | |
| const recipeHeader = document.createElement('div'); | |
| recipeHeader.className = 'flex justify-between items-start mb-4'; | |
| const recipeTitle = document.createElement('h3'); | |
| recipeTitle.className = 'text-xl font-bold text-gray-800'; | |
| recipeTitle.textContent = cookbook.toc.find(c => c.title === chapterTitle).recipes[index]; | |
| const regenerateBtn = document.createElement('button'); | |
| regenerateBtn.className = 'px-3 py-1 bg-gray-100 text-gray-700 rounded text-sm hover:bg-gray-200 transition-colors'; | |
| regenerateBtn.innerHTML = '<i class="fas fa-sync-alt mr-1"></i> Regenerate'; | |
| regenerateBtn.addEventListener('click', () => regenerateSingleRecipe(chapterTitle, index)); | |
| recipeHeader.appendChild(recipeTitle); | |
| recipeHeader.appendChild(regenerateBtn); | |
| const recipeContent = document.createElement('div'); | |
| recipeContent.className = 'prose max-w-none markdown-content'; | |
| recipeContent.innerHTML = marked.parse(recipe); | |
| recipeCard.appendChild(recipeHeader); | |
| recipeCard.appendChild(recipeContent); | |
| recipeList.appendChild(recipeCard); | |
| }); | |
| } | |
| function regenerateSingleRecipe(chapterTitle, recipeIndex) { | |
| const recipeName = cookbook.toc.find(c => c.title === chapterTitle).recipes[recipeIndex]; | |
| // Show loading state for this recipe | |
| const recipeCards = document.querySelectorAll('.recipe-card'); | |
| const currentCard = recipeCards[recipeIndex]; | |
| currentCard.style.opacity = '0.5'; | |
| currentCard.querySelector('button').innerHTML = '<i class="fas fa-spinner fa-spin mr-1"></i> Regenerating'; | |
| // Simulate API call with timeout | |
| setTimeout(() => { | |
| cookbook.recipes[chapterTitle][recipeIndex] = generateSampleRecipe(recipeName); | |
| // Update display | |
| const recipeContent = currentCard.querySelector('.prose'); | |
| recipeContent.innerHTML = marked.parse(cookbook.recipes[chapterTitle][recipeIndex]); | |
| // Restore card | |
| currentCard.style.opacity = '1'; | |
| currentCard.querySelector('button').innerHTML = '<i class="fas fa-sync-alt mr-1"></i> Regenerate'; | |
| }, 1000); | |
| } | |
| // Step 5: Front & Back Matter | |
| document.getElementById('back-to-step4').addEventListener('click', () => showStep(4)); | |
| document.getElementById('generate-matter').addEventListener('click', () => { | |
| // Show loading state | |
| document.getElementById('generate-matter-text').classList.add('hidden'); | |
| document.getElementById('generate-matter-spinner').classList.remove('hidden'); | |
| // Simulate API call with timeout | |
| setTimeout(() => { | |
| // Generate sample content | |
| cookbook.frontMatter = { | |
| introduction: generateSectionContent('Introduction', cookbook.theme), | |
| pantry: generateSectionContent('Pantry Essentials', cookbook.theme), | |
| tips: generateSectionContent('Cooking Tips', cookbook.theme) | |
| }; | |
| cookbook.backMatter = { | |
| conclusion: generateSectionContent('Conclusion', cookbook.theme), | |
| index: generateRecipeIndex(), | |
| conversions: generateConversionChart() | |
| }; | |
| // Display content | |
| displayMatterContent(); | |
| // Hide loading state | |
| document.getElementById('generate-matter-text').classList.remove('hidden'); | |
| document.getElementById('generate-matter-spinner').classList.add('hidden'); | |
| // Show results | |
| document.getElementById('matter-results').classList.remove('hidden'); | |
| }, 2000); | |
| }); | |
| document.getElementById('regenerate-matter').addEventListener('click', () => { | |
| // Show loading state | |
| document.getElementById('generate-matter-text').classList.add('hidden'); | |
| document.getElementById('generate-matter-spinner').classList.remove('hidden'); | |
| // Simulate API call with timeout | |
| setTimeout(() => { | |
| // Generate new sample content | |
| cookbook.frontMatter = { | |
| introduction: generateSectionContent('Introduction', cookbook.theme), | |
| pantry: generateSectionContent('Pantry Essentials', cookbook.theme), | |
| tips: generateSectionContent('Cooking Tips', cookbook.theme) | |
| }; | |
| cookbook.backMatter = { | |
| conclusion: generateSectionContent('Conclusion', cookbook.theme), | |
| index: generateRecipeIndex(), | |
| conversions: generateConversionChart() | |
| }; | |
| // Display content | |
| displayMatterContent(); | |
| // Hide loading state | |
| document.getElementById('generate-matter-text').classList.remove('hidden'); | |
| document.getElementById('generate-matter-spinner').classList.add('hidden'); | |
| }, 2000); | |
| }); | |
| document.getElementById('next-to-step6').addEventListener('click', () => showStep(6)); | |
| function generateSectionContent(title, theme = '') { | |
| if (title === 'Introduction') { | |
| return `# Introduction | |
| Welcome to "${cookbook.title}", your comprehensive guide to ${theme ? theme.toLowerCase() + ' cooking' : 'delicious recipes'}. This cookbook is designed to help you create amazing dishes with ease, whether you're a beginner or an experienced cook. | |
| ## About This Book | |
| In these pages, you'll find a carefully curated collection of recipes that ${theme ? 'celebrate the flavors of ' + theme : 'span a wide range of cuisines and techniques'}. Each recipe has been tested to ensure success in your kitchen, with clear instructions and helpful tips. | |
| ## How to Use This Cookbook | |
| 1. **Start Simple**: Begin with the easier recipes to build confidence | |
| 2. **Read Through First**: Always review the entire recipe before starting | |
| 3. **Prep Ahead**: Measure ingredients before you begin cooking | |
| 4. **Experiment**: Once comfortable, try variations and substitutions | |
| We hope this book becomes a trusted resource in your kitchen, inspiring many memorable meals with family and friends. Happy cooking!`; | |
| } else if (title === 'Pantry Essentials') { | |
| return `# Pantry Essentials | |
| Having a well-stocked pantry makes cooking easier and more enjoyable. Here are the essential ingredients we recommend keeping on hand for ${theme ? theme.toLowerCase() + ' cooking' : 'the recipes in this book'}. | |
| ## Dry Goods | |
| - All-purpose flour | |
| - Granulated sugar | |
| - Brown sugar | |
| - Baking powder | |
| - Baking soda | |
| - Cornstarch | |
| - Rice (white and brown) | |
| - Pasta (various shapes) | |
| - Dried beans and lentils | |
| ## Oils & Vinegars | |
| - Extra virgin olive oil | |
| - Neutral oil (like canola or vegetable) | |
| - Toasted sesame oil | |
| - Balsamic vinegar | |
| - Red wine vinegar | |
| - Rice vinegar | |
| ## Spices & Herbs | |
| ${theme === 'Italian' ? '- Dried oregano\n- Dried basil\n- Red pepper flakes\n- Garlic powder\n- Fennel seeds' : | |
| theme === 'Mexican' ? '- Ground cumin\n- Chili powder\n- Smoked paprika\n- Dried oregano\n- Cayenne pepper' : | |
| '- Ground cumin\n- Paprika\n- Garlic powder\n- Onion powder\n- Dried thyme\n- Bay leaves'} | |
| ## Canned Goods | |
| - Tomatoes (diced, crushed, and paste) | |
| - Coconut milk | |
| - Broths (chicken, vegetable, beef) | |
| - Canned beans (chickpeas, black beans, etc.) | |
| - Tuna or salmon (in water)`; | |
| } else if (title === 'Cooking Tips') { | |
| return `# Cooking Tips | |
| These professional techniques will help you get the best results from your ${theme ? theme.toLowerCase() + ' recipes' : 'cooking'}. | |
| ## Knife Skills | |
| 1. **Keep knives sharp**: A sharp knife is safer than a dull one | |
| 2. **Use the right knife**: Chef's knife for most tasks, paring for detail work | |
| 3. **Claw grip**: Curl fingers under when chopping to protect them | |
| 4. **Rocking motion**: Use the full length of the blade for efficient chopping | |
| ## Cooking Methods | |
| - **Searing**: Get the pan hot before adding food for better browning | |
| - **Simmering**: Keep liquids at a gentle bubble, not a rolling boil | |
| - **Resting meat**: Let cooked meat rest before slicing to retain juices | |
| - **Tasting as you go**: Adjust seasoning throughout the cooking process | |
| ## Time-Saving Tricks | |
| 1. **Mise en place**: Prepare all ingredients before starting to cook | |
| 2. **Batch cooking**: Make double portions and freeze half | |
| 3. **Multitask**: Start longer-cooking items first | |
| 4. **Clean as you go**: Wash tools while waiting for things to cook`; | |
| } else if (title === 'Conclusion') { | |
| return `# Conclusion | |
| Congratulations on completing your journey through "${cookbook.title}"! We hope you've enjoyed exploring ${theme ? 'the flavors of ' + theme : 'these diverse recipes'} and have discovered new favorite dishes along the way. | |
| ## Final Thoughts | |
| Cooking is an ongoing adventure, and each meal is an opportunity to learn and grow. Don't be discouraged by mistakes—they're valuable lessons that make you a better cook. The recipes in this book are just the beginning; feel free to adapt them to your taste and experiment with new ingredients. | |
| ## Share the Joy | |
| Some of life's best moments happen around the table. Share your culinary creations with others, teach someone to cook, and most importantly, savor the process as much as the results. | |
| Happy cooking, and may your kitchen always be filled with delicious aromas and joyful moments!`; | |
| } else if (title === 'Recipe Index') { | |
| return `# Recipe Index | |
| ## By Chapter | |
| ${cookbook.toc.map(chapter => `### ${chapter.title}\n${chapter.recipes.map(r => `- ${r}`).join('\n')}`).join('\n\n')} | |
| ## By Category | |
| ### Appetizers | |
| - Recipe 1 - Bruschetta | |
| - Recipe 2 - Stuffed Mushrooms | |
| ### Main Dishes | |
| - Recipe 3 - Chicken Parmesan | |
| - Recipe 4 - Vegetable Lasagna | |
| ### Desserts | |
| - Recipe 5 - Chocolate Cake | |
| - Recipe 6 - Tiramisu`; | |
| } else if (title === 'Measurement Conversions') { | |
| return `# Measurement Conversions | |
| ## Volume | |
| | Metric | Imperial | | |
| |--------|----------| | |
| | 5 ml | 1 tsp | | |
| | 15 ml | 1 tbsp | | |
| | 240 ml | 1 cup | | |
| | 1 liter| 4.2 cups | | |
| ## Weight | |
| | Metric | Imperial | | |
| |--------|----------| | |
| | 28 g | 1 oz | | |
| | 454 g | 1 lb | | |
| | 1 kg | 2.2 lb | | |
| ## Oven Temperatures | |
| | °C | °F | | |
| |-----|-------| | |
| | 120 | 250 | | |
| | 150 | 300 | | |
| | 180 | 350 | | |
| | 200 | 400 | | |
| | 220 | 425 | | |
| | 240 | 475 |`; | |
| } | |
| return `# ${title}\n\nSample content for ${title} section.`; | |
| } | |
| function generateRecipeIndex() { | |
| let indexContent = `# Recipe Index\n\n`; | |
| // By Chapter | |
| indexContent += `## By Chapter\n`; | |
| cookbook.toc.forEach(chapter => { | |
| indexContent += `### ${chapter.title}\n`; | |
| chapter.recipes.forEach(recipe => { | |
| indexContent += `- ${recipe}\n`; | |
| }); | |
| indexContent += `\n`; | |
| }); | |
| // By Category (simplified) | |
| indexContent += `## By Category\n`; | |
| indexContent += `### Main Dishes\n`; | |
| cookbook.toc.slice(0, 3).forEach(chapter => { | |
| chapter.recipes.slice(0, 2).forEach(recipe => { | |
| indexContent += `- ${recipe}\n`; | |
| }); | |
| }); | |
| indexContent += `\n### Desserts\n`; | |
| cookbook.toc.slice(-1).forEach(chapter => { | |
| chapter.recipes.slice(0, 3).forEach(recipe => { | |
| indexContent += `- ${recipe}\n`; | |
| }); | |
| }); | |
| return indexContent; | |
| } | |
| function generateConversionChart() { | |
| return `# Measurement Conversions | |
| ## Volume Conversions | |
| | Metric | US Standard | | |
| |--------|-------------| | |
| | 1 ml | 1/4 tsp | | |
| | 5 ml | 1 tsp | | |
| | 15 ml | 1 tbsp | | |
| | 30 ml | 1 fl oz | | |
| | 240 ml | 1 cup | | |
| | 1 liter| 4.2 cups | | |
| ## Weight Conversions | |
| | Metric | US Standard | | |
| |--------|-------------| | |
| | 1 g | 0.035 oz | | |
| | 28 g | 1 oz | | |
| | 100 g | 3.5 oz | | |
| | 454 g | 1 lb | | |
| | 1 kg | 2.2 lb | | |
| ## Oven Temperature Conversions | |
| | °C | °F | Gas Mark | | |
| |-----|-------|----------| | |
| | 110 | 225 | 1/4 | | |
| | 120 | 250 | 1/2 | | |
| | 140 | 275 | 1 | | |
| | 150 | 300 | 2 | | |
| | 170 | 325 | 3 | | |
| | 180 | 350 | 4 | | |
| | 190 | 375 | 5 | | |
| | 200 | 400 | 6 | | |
| | 220 | 425 | 7 | | |
| | 230 | 450 | 8 | | |
| | 240 | 475 | 9 |`; | |
| } | |
| function displayMatterContent() { | |
| const matterPreview = document.getElementById('matter-preview'); | |
| matterPreview.innerHTML = ''; | |
| // Front matter | |
| if (document.getElementById('generate-intro').checked && cookbook.frontMatter.introduction) { | |
| const introDiv = document.createElement('div'); | |
| introDiv.className = 'border border-gray-200 rounded-lg p-4 bg-gray-50'; | |
| const introHeader = document.createElement('div'); | |
| introHeader.className = 'flex justify-between items-center mb-3'; | |
| const introTitle = document.createElement('h4'); | |
| introTitle.className = 'text-lg font-bold text-gray-800'; | |
| introTitle.textContent = 'Introduction'; | |
| introHeader.appendChild(introTitle); | |
| introDiv.appendChild(introHeader); | |
| const introContent = document.createElement('div'); | |
| introContent.className = 'prose max-w-none markdown-content'; | |
| introContent.innerHTML = marked.parse(cookbook.frontMatter.introduction); | |
| introDiv.appendChild(introContent); | |
| matterPreview.appendChild(introDiv); | |
| } | |
| if (document.getElementById('generate-pantry').checked && cookbook.frontMatter.pantry) { | |
| const pantryDiv = document.createElement('div'); | |
| pantryDiv.className = 'border border-gray-200 rounded-lg p-4 bg-gray-50'; | |
| const pantryHeader = document.createElement('div'); | |
| pantryHeader.className = 'flex justify-between items-center mb-3'; | |
| const pantryTitle = document.createElement('h4'); | |
| pantryTitle.className = 'text-lg font-bold text-gray-800'; | |
| pantryTitle.textContent = 'Pantry Essentials'; | |
| pantryHeader.appendChild(pantryTitle); | |
| pantryDiv.appendChild(pantryHeader); | |
| const pantryContent = document.createElement('div'); | |
| pantryContent.className = 'prose max-w-none markdown-content'; | |
| pantryContent.innerHTML = marked.parse(cookbook.frontMatter.pantry); | |
| pantryDiv.appendChild(pantryContent); | |
| matterPreview.appendChild(pantryDiv); | |
| } | |
| if (document.getElementById('generate-tips').checked && cookbook.frontMatter.tips) { | |
| const tipsDiv = document.createElement('div'); | |
| tipsDiv.className = 'border border-gray-200 rounded-lg p-4 bg-gray-50'; | |
| const tipsHeader = document.createElement('div'); | |
| tipsHeader.className = 'flex justify-between items-center mb-3'; | |
| const tipsTitle = document.createElement('h4'); | |
| tipsTitle.className = 'text-lg font-bold text-gray-800'; | |
| tipsTitle.textContent = 'Cooking Tips'; | |
| tipsHeader.appendChild(tipsTitle); | |
| tipsDiv.appendChild(tipsHeader); | |
| const tipsContent = document.createElement('div'); | |
| tipsContent.className = 'prose max-w-none markdown-content'; | |
| tipsContent.innerHTML = marked.parse(cookbook.frontMatter.tips); | |
| tipsDiv.appendChild(tipsContent); | |
| matterPreview.appendChild(tipsDiv); | |
| } | |
| // Back matter | |
| if (document.getElementById('generate-conclusion').checked && cookbook.backMatter.conclusion) { | |
| const conclusionDiv = document.createElement('div'); | |
| conclusionDiv.className = 'border border-gray-200 rounded-lg p-4 bg-gray-50'; | |
| const conclusionHeader = document.createElement('div'); | |
| conclusionHeader.className = 'flex justify-between items-center mb-3'; | |
| const conclusionTitle = document.createElement('h4'); | |
| conclusionTitle.className = 'text-lg font-bold text-gray-800'; | |
| conclusionTitle.textContent = 'Conclusion'; | |
| conclusionHeader.appendChild(conclusionTitle); | |
| conclusionDiv.appendChild(conclusionHeader); | |
| const conclusionContent = document.createElement('div'); | |
| conclusionContent.className = 'prose max-w-none markdown-content'; | |
| conclusionContent.innerHTML = marked.parse(cookbook.backMatter.conclusion); | |
| conclusionDiv.appendChild(conclusionContent); | |
| matterPreview.appendChild(conclusionDiv); | |
| } | |
| if (document.getElementById('generate-index').checked && cookbook.backMatter.index) { | |
| const indexDiv = document.createElement('div'); | |
| indexDiv.className = 'border border-gray-200 rounded-lg p-4 bg-gray-50'; | |
| const indexHeader = document.createElement('div'); | |
| indexHeader.className = 'flex justify-between items-center mb-3'; | |
| const indexTitle = document.createElement('h4'); | |
| indexTitle.className = 'text-lg font-bold text-gray-800'; | |
| indexTitle.textContent = 'Recipe Index'; | |
| indexHeader.appendChild(indexTitle); | |
| indexDiv.appendChild(indexHeader); | |
| const indexContent = document.createElement('div'); | |
| indexContent.className = 'prose max-w-none markdown-content'; | |
| indexContent.innerHTML = marked.parse(cookbook.backMatter.index); | |
| indexDiv.appendChild(indexContent); | |
| matterPreview.appendChild(indexDiv); | |
| } | |
| if (document.getElementById('generate-conversion').checked && cookbook.backMatter.conversions) { | |
| const conversionDiv = document.createElement('div'); | |
| conversionDiv.className = 'border border-gray-200 rounded-lg p-4 bg-gray-50'; | |
| const conversionHeader = document.createElement('div'); | |
| conversionHeader.className = 'flex justify-between items-center mb-3'; | |
| const conversionTitle = document.createElement('h4'); | |
| conversionTitle.className = 'text-lg font-bold text-gray-800'; | |
| conversionTitle.textContent = 'Measurement Conversions'; | |
| conversionHeader.appendChild(conversionTitle); | |
| conversionDiv.appendChild(conversionHeader); | |
| const conversionContent = document.createElement('div'); | |
| conversionContent.className = 'prose max-w-none markdown-content'; | |
| conversionContent.innerHTML = marked.parse(cookbook.backMatter.conversions); | |
| conversionDiv.appendChild(conversionContent); | |
| matterPreview.appendChild(conversionDiv); | |
| } | |
| } | |
| // Step 6: Export | |
| document.getElementById('back-to-step5').addEventListener('click', () => showStep(5)); | |
| document.querySelectorAll('.export-option').forEach(option => { | |
| option.addEventListener('click', function() { | |
| // Remove active class from all options | |
| document.querySelectorAll('.export-option').forEach(opt => { | |
| opt.classList.remove('border-blue-500', 'bg-blue-100'); | |
| }); | |
| // Add active class to selected option | |
| this.classList.add('border-blue-500', 'bg-blue-100'); | |
| // Store selected format | |
| document.getElementById('export-book').dataset.format = this.dataset.format; | |
| }); | |
| }); | |
| document.getElementById('export-book').addEventListener('click', function() { | |
| const format = this.dataset.format; | |
| if (!format) { | |
| alert('Please select an export format'); | |
| return; | |
| } | |
| // Show loading state | |
| document.getElementById('export-book-text').classList.add('hidden'); | |
| document.getElementById('export-book-spinner').classList.remove('hidden'); | |
| // Simulate export process | |
| setTimeout(() => { | |
| // Hide loading state | |
| document.getElementById('export-book-text').classList.remove('hidden'); | |
| document.getElementById('export-book-spinner').classList.add('hidden'); | |
| // Show preview modal | |
| showPreviewModal(); | |
| }, 1500); | |
| }); | |
| function showPreviewModal() { | |
| const modal = document.getElementById('preview-modal'); | |
| const previewContent = document.getElementById('full-preview-content'); | |
| // Generate full preview content | |
| let fullContent = ''; | |
| // Add title page | |
| fullContent += `# ${cookbook.title}\n\n`; | |
| fullContent += `## A Cookbook by You\n\n`; | |
| fullContent += `---\n\n`; | |
| // Add front matter | |
| if (cookbook.frontMatter.introduction) { | |
| fullContent += cookbook.frontMatter.introduction + '\n\n'; | |
| } | |
| if (cookbook.frontMatter.pantry) { | |
| fullContent += cookbook.frontMatter.pantry + '\n\n'; | |
| } | |
| if (cookbook.frontMatter.tips) { | |
| fullContent += cookbook.frontMatter.tips + '\n\n'; | |
| } | |
| // Add TOC | |
| fullContent += `# Table of Contents\n\n`; | |
| cookbook.toc.forEach((chapter, index) => { | |
| fullContent += `## Chapter ${index + 1}: ${chapter.title}\n`; | |
| chapter.recipes.forEach(recipe => { | |
| fullContent += `- ${recipe}\n`; | |
| }); | |
| fullContent += `\n`; | |
| }); | |
| fullContent += `---\n\n`; | |
| // Add recipes | |
| cookbook.toc.forEach(chapter => { | |
| fullContent += `# ${chapter.title}\n\n`; | |
| if (cookbook.recipes[chapter.title]) { | |
| cookbook.recipes[chapter.title].forEach(recipe => { | |
| if (recipe) { | |
| fullContent += recipe + '\n\n---\n\n'; | |
| } | |
| }); | |
| } | |
| }); | |
| // Add back matter | |
| if (cookbook.backMatter.conclusion) { | |
| fullContent += cookbook.backMatter.conclusion + '\n\n'; | |
| } | |
| if (cookbook.backMatter.index) { | |
| fullContent += cookbook.backMatter.index + '\n\n'; | |
| } | |
| if (cookbook.backMatter.conversions) { | |
| fullContent += cookbook.backMatter.conversions + '\n\n'; | |
| } | |
| // Render markdown | |
| previewContent.innerHTML = marked.parse(fullContent); | |
| // Show modal | |
| modal.classList.remove('hidden'); | |
| } | |
| document.getElementById('close-preview').addEventListener('click', () => { | |
| document.getElementById('preview-modal').classList.add('hidden'); | |
| }); | |
| document.getElementById('export-from-preview').addEventListener('click', () => { | |
| alert('Export functionality would be implemented here. In a real app, this would generate a Word or PDF file.'); | |
| document.getElementById('preview-modal').classList.add('hidden'); | |
| }); | |
| // Marked.js for markdown rendering | |
| const marked = { | |
| parse: function(markdown) { | |
| // Simple markdown to HTML conversion (in a real app, use the actual marked.js library) | |
| let html = markdown; | |
| // Headers | |
| html = html.replace(/^# (.*$)/gm, '<h1>$1</h1>'); | |
| html = html.replace(/^## (.*$)/gm, '<h2>$1</h2>'); | |
| html = html.replace(/^### (.*$)/gm, '<h3>$1</h3>'); | |
| // Lists | |
| html = html.replace(/^\* (.*$)/gm, '<li>$1</li>'); | |
| html = html.replace(/^- (.*$)/gm, '<li>$1</li>'); | |
| html = html.replace(/^(\d+)\. (.*$)/gm, '<li>$2</li>'); | |
| // Paragraphs | |
| html = html.replace(/^(?!<[a-z])(.*$)/gm, function(m) { | |
| return m.trim() ? '<p>' + m + '</p>' : ''; | |
| }); | |
| // Simple line breaks | |
| html = html.replace(/\n/g, '<br>'); | |
| return html; | |
| } | |
| }; | |
| // Initialize app | |
| showStep(1); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=nyaringe/practical-models" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |