abidlabs HF Staff commited on
Commit
adc9dea
·
1 Parent(s): 5b3b29e
RECIPE_GENERATOR.md ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ClosedToOpen Recipe Generator
2
+
3
+ This script converts Jupyter notebooks to recipe pages using [nbgradio](https://github.com/gradio-app/nbgradio).
4
+
5
+ ## Usage
6
+
7
+ ### Using uv (Recommended)
8
+
9
+ ```bash
10
+ uv run generate_recipes.py
11
+ ```
12
+
13
+ ### Direct execution
14
+
15
+ ```bash
16
+ ./generate_recipes.py
17
+ ```
18
+
19
+ ## What it does
20
+
21
+ 1. **Scans notebooks**: Finds all `.ipynb` files in the `notebooks/` directory
22
+ 2. **Updates notebooks**: Adds `#nbgradio` comments to cells containing Gradio code
23
+ 3. **Converts to recipes**: Uses nbgradio with `--spaces` and `--fragment` flags to generate HTML
24
+ 4. **Creates recipe pages**: Wraps the generated content in the blog's styling
25
+ 5. **Deploys to Spaces**: Automatically deploys Gradio apps to Hugging Face Spaces
26
+
27
+ ## Features
28
+
29
+ - **Automatic dependency management**: Uses uv's inline script metadata
30
+ - **Spaces integration**: Deploys interactive apps to Hugging Face Spaces
31
+ - **Blog integration**: Generates pages that match the site's design
32
+ - **Incremental updates**: Skips notebooks that already have recipes
33
+ - **Error handling**: Graceful handling of conversion failures
34
+
35
+ ## Requirements
36
+
37
+ - Python 3.10+
38
+ - uv package manager
39
+ - Hugging Face account (for Spaces deployment)
40
+
41
+ ## Generated Files
42
+
43
+ - `recipes/{notebook_name}.html` - Complete recipe pages
44
+ - Interactive Gradio apps deployed to Hugging Face Spaces
45
+ - Proper styling integration with the main blog
46
+
47
+ ## Script Dependencies
48
+
49
+ The script uses uv's inline metadata format to declare dependencies:
50
+
51
+ ```python
52
+ # /// script
53
+ # dependencies = [
54
+ # "nbgradio",
55
+ # ]
56
+ # ///
57
+ ```
58
+
59
+ This ensures nbgradio is automatically installed when running the script.
convert_notebooks.py ADDED
@@ -0,0 +1,223 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Script to convert Jupyter notebooks to recipe pages using nbgradio.
4
+ This script processes notebooks in the notebooks/ directory and generates
5
+ recipe pages in the recipes/ directory with proper styling integration.
6
+ """
7
+
8
+ import os
9
+ import subprocess
10
+ import sys
11
+ from pathlib import Path
12
+ import shutil
13
+ import json
14
+
15
+ def install_nbgradio():
16
+ """Install nbgradio if not already installed."""
17
+ try:
18
+ import nbgradio
19
+ print("✅ nbgradio is already installed")
20
+ except ImportError:
21
+ print("📦 Installing nbgradio...")
22
+ subprocess.run([sys.executable, "-m", "pip", "install", "nbgradio"], check=True)
23
+ print("✅ nbgradio installed successfully")
24
+
25
+ def get_notebook_files():
26
+ """Get all notebook files from the notebooks directory."""
27
+ notebooks_dir = Path("notebooks")
28
+ if not notebooks_dir.exists():
29
+ print("❌ notebooks/ directory not found")
30
+ return []
31
+
32
+ notebook_files = list(notebooks_dir.glob("*.ipynb"))
33
+ print(f"📚 Found {len(notebook_file)} notebook(s): {[f.name for f in notebook_files]}")
34
+ return notebook_files
35
+
36
+ def get_existing_recipes():
37
+ """Get list of existing recipe files."""
38
+ recipes_dir = Path("recipes")
39
+ if not recipes_dir.exists():
40
+ return []
41
+
42
+ recipe_files = list(recipes_dir.glob("*.html"))
43
+ return [f.stem for f in recipe_files]
44
+
45
+ def convert_notebook_to_recipe(notebook_path):
46
+ """Convert a single notebook to a recipe page."""
47
+ notebook_name = notebook_path.stem
48
+ print(f"\n🔄 Converting {notebook_name}...")
49
+
50
+ # Create temporary directory for nbgradio output
51
+ temp_dir = Path(f"temp_{notebook_name}")
52
+ temp_dir.mkdir(exist_ok=True)
53
+
54
+ try:
55
+ # Run nbgradio with --spaces and --fragment
56
+ cmd = [
57
+ "nbgradio", "build",
58
+ str(notebook_path),
59
+ "--spaces",
60
+ "--fragment",
61
+ "--output-dir", str(temp_dir)
62
+ ]
63
+
64
+ print(f"🚀 Running: {' '.join(cmd)}")
65
+ result = subprocess.run(cmd, capture_output=True, text=True)
66
+
67
+ if result.returncode != 0:
68
+ print(f"❌ Error converting {notebook_name}:")
69
+ print(result.stderr)
70
+ return False
71
+
72
+ print(f"✅ Successfully converted {notebook_name}")
73
+
74
+ # Process the generated fragment
75
+ fragment_file = temp_dir / "fragments" / f"{notebook_name}.html"
76
+ if fragment_file.exists():
77
+ create_recipe_page(notebook_name, fragment_file)
78
+ return True
79
+ else:
80
+ print(f"❌ Fragment file not found: {fragment_file}")
81
+ return False
82
+
83
+ except Exception as e:
84
+ print(f"❌ Error processing {notebook_name}: {e}")
85
+ return False
86
+ finally:
87
+ # Clean up temporary directory
88
+ if temp_dir.exists():
89
+ shutil.rmtree(temp_dir)
90
+
91
+ def create_recipe_page(notebook_name, fragment_file):
92
+ """Create a complete recipe page from the fragment."""
93
+ recipes_dir = Path("recipes")
94
+ recipes_dir.mkdir(exist_ok=True)
95
+
96
+ # Read the fragment content
97
+ with open(fragment_file, 'r', encoding='utf-8') as f:
98
+ fragment_content = f.read()
99
+
100
+ # Extract title from notebook name (convert snake_case to Title Case)
101
+ title = notebook_name.replace('_', ' ').replace('-', ' ').title()
102
+
103
+ # Create the complete recipe page
104
+ recipe_html = f"""<!doctype html>
105
+ <html>
106
+ <head>
107
+ <meta charset="utf-8" />
108
+ <meta name="viewport" content="width=device-width" />
109
+ <title>{title} - ClosedToOpen</title>
110
+ <link rel="stylesheet" href="../style.css" />
111
+ </head>
112
+ <body>
113
+ <nav class="navbar">
114
+ <div class="nav-content">
115
+ <h1 class="nav-title">ClosedToOpen</h1>
116
+ <p class="nav-subtitle">how to stop relying on proprietary APIs for AI and start living freely</p>
117
+ </div>
118
+ </nav>
119
+
120
+ <main class="main-content">
121
+ <div class="breadcrumb">
122
+ <a href="../index.html">← Back to Migration Guides</a>
123
+ </div>
124
+
125
+ <section class="recipe-header">
126
+ <h1 class="recipe-title">{title}</h1>
127
+ <p class="recipe-description">Migrate from proprietary APIs to open-source alternatives</p>
128
+ <div class="recipe-meta">
129
+ <span class="difficulty">Difficulty: Medium</span>
130
+ <span class="time">Time: 45 minutes</span>
131
+ <span class="category">Category: Migration</span>
132
+ </div>
133
+ </section>
134
+
135
+ <section class="recipe-content">
136
+ <div class="notebook-content">
137
+ {fragment_content}
138
+ </div>
139
+ </section>
140
+ </main>
141
+ </body>
142
+ </html>"""
143
+
144
+ # Write the recipe page
145
+ recipe_file = recipes_dir / f"{notebook_name}.html"
146
+ with open(recipe_file, 'w', encoding='utf-8') as f:
147
+ f.write(recipe_html)
148
+
149
+ print(f"📄 Created recipe page: {recipe_file}")
150
+
151
+ def update_notebook_for_nbgradio(notebook_path):
152
+ """Update notebook to include nbgradio syntax for interactive cells."""
153
+ print(f"🔧 Updating {notebook_path.name} for nbgradio...")
154
+
155
+ # Read the notebook
156
+ with open(notebook_path, 'r', encoding='utf-8') as f:
157
+ notebook = json.load(f)
158
+
159
+ updated = False
160
+
161
+ # Look for cells with gradio code and add nbgradio comments
162
+ for cell in notebook['cells']:
163
+ if cell['cell_type'] == 'code':
164
+ source = ''.join(cell['source'])
165
+
166
+ # Check if this cell contains gradio code
167
+ if 'import gradio' in source or 'gr.Interface' in source or 'demo.launch()' in source:
168
+ # Check if it already has nbgradio comment
169
+ if not source.startswith('#nbgradio'):
170
+ # Add nbgradio comment
171
+ notebook_name = notebook_path.stem
172
+ nbgradio_comment = f"#nbgradio name=\"{notebook_name}\"\n"
173
+ cell['source'] = [nbgradio_comment] + cell['source']
174
+ updated = True
175
+ print(f" ✅ Added nbgradio comment to gradio cell")
176
+
177
+ # Write back if updated
178
+ if updated:
179
+ with open(notebook_path, 'w', encoding='utf-8') as f:
180
+ json.dump(notebook, f, indent=2)
181
+ print(f" 📝 Updated notebook saved")
182
+ else:
183
+ print(f" ℹ️ No gradio cells found or already updated")
184
+
185
+ def main():
186
+ """Main function to convert all notebooks to recipes."""
187
+ print("🚀 Starting notebook to recipe conversion...")
188
+
189
+ # Install nbgradio
190
+ install_nbgradio()
191
+
192
+ # Get notebook files
193
+ notebook_files = get_notebook_files()
194
+ if not notebook_files:
195
+ print("❌ No notebooks found to convert")
196
+ return
197
+
198
+ # Get existing recipes
199
+ existing_recipes = get_existing_recipes()
200
+ print(f"📋 Existing recipes: {existing_recipes}")
201
+
202
+ # Convert each notebook
203
+ converted_count = 0
204
+ for notebook_path in notebook_files:
205
+ notebook_name = notebook_path.stem
206
+
207
+ # Skip if recipe already exists
208
+ if notebook_name in existing_recipes:
209
+ print(f"⏭️ Skipping {notebook_name} (recipe already exists)")
210
+ continue
211
+
212
+ # Update notebook for nbgradio
213
+ update_notebook_for_nbgradio(notebook_path)
214
+
215
+ # Convert to recipe
216
+ if convert_notebook_to_recipe(notebook_path):
217
+ converted_count += 1
218
+
219
+ print(f"\n🎉 Conversion complete! {converted_count} new recipe(s) created.")
220
+ print("📁 Check the recipes/ directory for the generated files.")
221
+
222
+ if __name__ == "__main__":
223
+ main()
generate_recipes.py ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # /// script
3
+ # dependencies = [
4
+ # "nbgradio",
5
+ # ]
6
+ # ///
7
+
8
+ """
9
+ Script to convert Jupyter notebooks to recipe pages using nbgradio.
10
+ This script processes notebooks in the notebooks/ directory and generates
11
+ recipe pages in the recipes/ directory with proper styling integration.
12
+ """
13
+
14
+ import os
15
+ import subprocess
16
+ import sys
17
+ from pathlib import Path
18
+ import shutil
19
+ import json
20
+
21
+ def get_notebook_files():
22
+ """Get all notebook files from the notebooks directory."""
23
+ notebooks_dir = Path("notebooks")
24
+ if not notebooks_dir.exists():
25
+ print("❌ notebooks/ directory not found")
26
+ return []
27
+
28
+ notebook_files = list(notebooks_dir.glob("*.ipynb"))
29
+ print(f"📚 Found {len(notebook_files)} notebook(s): {[f.name for f in notebook_files]}")
30
+ return notebook_files
31
+
32
+ def get_existing_recipes():
33
+ """Get list of existing recipe files."""
34
+ recipes_dir = Path("recipes")
35
+ if not recipes_dir.exists():
36
+ return []
37
+
38
+ recipe_files = list(recipes_dir.glob("*.html"))
39
+ return [f.stem for f in recipe_files]
40
+
41
+ def convert_notebook_to_recipe(notebook_path):
42
+ """Convert a single notebook to a recipe page."""
43
+ notebook_name = notebook_path.stem
44
+ print(f"\n🔄 Converting {notebook_name}...")
45
+
46
+ # Create temporary directory for nbgradio output
47
+ temp_dir = Path(f"temp_{notebook_name}")
48
+ temp_dir.mkdir(exist_ok=True)
49
+
50
+ try:
51
+ # Run nbgradio with --spaces and --fragment
52
+ cmd = [
53
+ "nbgradio", "build",
54
+ str(notebook_path),
55
+ "--spaces",
56
+ "--fragment",
57
+ "--output-dir", str(temp_dir)
58
+ ]
59
+
60
+ print(f"🚀 Running: {' '.join(cmd)}")
61
+ result = subprocess.run(cmd, capture_output=True, text=True)
62
+
63
+ if result.returncode != 0:
64
+ print(f"❌ Error converting {notebook_name}:")
65
+ print(result.stderr)
66
+ return False
67
+
68
+ print(f"✅ Successfully converted {notebook_name}")
69
+
70
+ # Process the generated fragment
71
+ fragment_file = temp_dir / "fragments" / f"{notebook_name}.html"
72
+ if fragment_file.exists():
73
+ create_recipe_page(notebook_name, fragment_file)
74
+ return True
75
+ else:
76
+ print(f"❌ Fragment file not found: {fragment_file}")
77
+ return False
78
+
79
+ except Exception as e:
80
+ print(f"❌ Error processing {notebook_name}: {e}")
81
+ return False
82
+ finally:
83
+ # Clean up temporary directory
84
+ if temp_dir.exists():
85
+ shutil.rmtree(temp_dir)
86
+
87
+ def create_recipe_page(notebook_name, fragment_file):
88
+ """Create a complete recipe page from the fragment."""
89
+ recipes_dir = Path("recipes")
90
+ recipes_dir.mkdir(exist_ok=True)
91
+
92
+ # Read the fragment content
93
+ with open(fragment_file, 'r', encoding='utf-8') as f:
94
+ fragment_content = f.read()
95
+
96
+ # Extract title from notebook name (convert snake_case to Title Case)
97
+ title = notebook_name.replace('_', ' ').replace('-', ' ').title()
98
+
99
+ # Create the complete recipe page
100
+ recipe_html = f"""<!doctype html>
101
+ <html>
102
+ <head>
103
+ <meta charset="utf-8" />
104
+ <meta name="viewport" content="width=device-width" />
105
+ <title>{title} - ClosedToOpen</title>
106
+ <link rel="stylesheet" href="../style.css" />
107
+ </head>
108
+ <body>
109
+ <nav class="navbar">
110
+ <div class="nav-content">
111
+ <h1 class="nav-title">ClosedToOpen</h1>
112
+ <p class="nav-subtitle">how to stop relying on proprietary APIs for AI and start living freely</p>
113
+ </div>
114
+ </nav>
115
+
116
+ <main class="main-content">
117
+ <div class="breadcrumb">
118
+ <a href="../index.html">← Back to Migration Guides</a>
119
+ </div>
120
+
121
+ <section class="recipe-header">
122
+ <h1 class="recipe-title">{title}</h1>
123
+ <p class="recipe-description">Migrate from proprietary APIs to open-source alternatives</p>
124
+ <div class="recipe-meta">
125
+ <span class="difficulty">Difficulty: Medium</span>
126
+ <span class="time">Time: 45 minutes</span>
127
+ <span class="category">Category: Migration</span>
128
+ </div>
129
+ </section>
130
+
131
+ <section class="recipe-content">
132
+ <div class="notebook-content">
133
+ {fragment_content}
134
+ </div>
135
+ </section>
136
+ </main>
137
+ </body>
138
+ </html>"""
139
+
140
+ # Write the recipe page
141
+ recipe_file = recipes_dir / f"{notebook_name}.html"
142
+ with open(recipe_file, 'w', encoding='utf-8') as f:
143
+ f.write(recipe_html)
144
+
145
+ print(f"📄 Created recipe page: {recipe_file}")
146
+
147
+ def update_notebook_for_nbgradio(notebook_path):
148
+ """Update notebook to include nbgradio syntax for interactive cells."""
149
+ print(f"🔧 Updating {notebook_path.name} for nbgradio...")
150
+
151
+ # Read the notebook
152
+ with open(notebook_path, 'r', encoding='utf-8') as f:
153
+ notebook = json.load(f)
154
+
155
+ updated = False
156
+
157
+ # Look for cells with gradio code and add nbgradio comments
158
+ for cell in notebook['cells']:
159
+ if cell['cell_type'] == 'code':
160
+ source = ''.join(cell['source'])
161
+
162
+ # Check if this cell contains gradio code
163
+ if 'import gradio' in source or 'gr.Interface' in source or 'demo.launch()' in source:
164
+ # Check if it already has nbgradio comment
165
+ if not source.startswith('#nbgradio'):
166
+ # Add nbgradio comment
167
+ notebook_name = notebook_path.stem
168
+ nbgradio_comment = f"#nbgradio name=\"{notebook_name}\"\n"
169
+ cell['source'] = [nbgradio_comment] + cell['source']
170
+ updated = True
171
+ print(f" ✅ Added nbgradio comment to gradio cell")
172
+
173
+ # Write back if updated
174
+ if updated:
175
+ with open(notebook_path, 'w', encoding='utf-8') as f:
176
+ json.dump(notebook, f, indent=2)
177
+ print(f" 📝 Updated notebook saved")
178
+ else:
179
+ print(f" ℹ️ No gradio cells found or already updated")
180
+
181
+ def main():
182
+ """Main function to convert all notebooks to recipes."""
183
+ print("🚀 Starting notebook to recipe conversion...")
184
+
185
+ # Get notebook files
186
+ notebook_files = get_notebook_files()
187
+ if not notebook_files:
188
+ print("❌ No notebooks found to convert")
189
+ return
190
+
191
+ # Get existing recipes
192
+ existing_recipes = get_existing_recipes()
193
+ print(f"📋 Existing recipes: {existing_recipes}")
194
+
195
+ # Convert each notebook
196
+ converted_count = 0
197
+ for notebook_path in notebook_files:
198
+ notebook_name = notebook_path.stem
199
+
200
+ # Skip if recipe already exists
201
+ if notebook_name in existing_recipes:
202
+ print(f"⏭️ Skipping {notebook_name} (recipe already exists)")
203
+ continue
204
+
205
+ # Update notebook for nbgradio
206
+ update_notebook_for_nbgradio(notebook_path)
207
+
208
+ # Convert to recipe
209
+ if convert_notebook_to_recipe(notebook_path):
210
+ converted_count += 1
211
+
212
+ print(f"\n🎉 Conversion complete! {converted_count} new recipe(s) created.")
213
+ print("📁 Check the recipes/ directory for the generated files.")
214
+
215
+ if __name__ == "__main__":
216
+ main()
index.html CHANGED
@@ -52,11 +52,12 @@
52
  <div class="recipe-cell audio">
53
  <h3>Audio</h3>
54
  <div class="recipe-links">
55
- <a href="audio.html#whisper" class="recipe-link">Whisper API → Whisper.cpp</a>
 
56
  <a href="audio.html#tts" class="recipe-link">Text-to-Speech → Bark</a>
57
  <a href="audio.html#classification" class="recipe-link">Audio Classification</a>
58
  <a href="audio.html#music" class="recipe-link">Music Generation</a>
59
- <a href="audio.html" class="recipe-link more-recipes">+ 3 more recipes</a>
60
  </div>
61
  </div>
62
  <div class="recipe-cell video">
@@ -116,6 +117,73 @@
116
  </div>
117
  </div>
118
  </section>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
  </main>
120
  </body>
121
  </html>
 
52
  <div class="recipe-cell audio">
53
  <h3>Audio</h3>
54
  <div class="recipe-links">
55
+ <a href="recipes/whisper_api_to_whisper_cpp.html" class="recipe-link">Whisper API → Whisper.cpp</a>
56
+ <a href="recipes/test_notebook.html" class="recipe-link">Test Notebook Demo</a>
57
  <a href="audio.html#tts" class="recipe-link">Text-to-Speech → Bark</a>
58
  <a href="audio.html#classification" class="recipe-link">Audio Classification</a>
59
  <a href="audio.html#music" class="recipe-link">Music Generation</a>
60
+ <a href="audio.html" class="recipe-link more-recipes">+ 2 more recipes</a>
61
  </div>
62
  </div>
63
  <div class="recipe-cell video">
 
117
  </div>
118
  </div>
119
  </section>
120
+
121
+ <section class="picks-section">
122
+ <h2 class="section-title">Abubakar's Picks</h2>
123
+ <p class="picks-subtitle">My curated recommendations for the best open-source models across different AI tasks</p>
124
+
125
+ <div class="picks-grid">
126
+ <div class="pick-category">
127
+ <h3>🎵 Audio Processing</h3>
128
+ <ul class="pick-list">
129
+ <li><strong>Whisper.cpp</strong> - Best for speech-to-text, runs locally</li>
130
+ <li><strong>Bark</strong> - Excellent text-to-speech with emotion</li>
131
+ <li><strong>MusicGen</strong> - Meta's music generation model</li>
132
+ <li><strong>AudioCraft</strong> - Facebook's audio generation suite</li>
133
+ </ul>
134
+ </div>
135
+
136
+ <div class="pick-category">
137
+ <h3>🎬 Video Processing</h3>
138
+ <ul class="pick-list">
139
+ <li><strong>Video-LLaMA</strong> - Best for video understanding</li>
140
+ <li><strong>Stable Video Diffusion</strong> - Top-tier video generation</li>
141
+ <li><strong>AnimateDiff</strong> - Great for character animation</li>
142
+ <li><strong>RunwayML</strong> - Professional video editing AI</li>
143
+ </ul>
144
+ </div>
145
+
146
+ <div class="pick-category">
147
+ <h3>🖼️ Multimodal</h3>
148
+ <ul class="pick-list">
149
+ <li><strong>LLaVA</strong> - Best open-source vision-language model</li>
150
+ <li><strong>Stable Diffusion XL</strong> - Superior image generation</li>
151
+ <li><strong>BLIP-2</strong> - Excellent image captioning</li>
152
+ <li><strong>InstructBLIP</strong> - Great for visual Q&A</li>
153
+ </ul>
154
+ </div>
155
+
156
+ <div class="pick-category">
157
+ <h3>📝 Text Generation</h3>
158
+ <ul class="pick-list">
159
+ <li><strong>Llama 3</strong> - Best overall performance</li>
160
+ <li><strong>Mistral 7B</strong> - Fast and efficient</li>
161
+ <li><strong>Code Llama</strong> - Superior code generation</li>
162
+ <li><strong>Phi-3</strong> - Microsoft's efficient model</li>
163
+ </ul>
164
+ </div>
165
+
166
+ <div class="pick-category">
167
+ <h3>🔒 Safety & Guardrails</h3>
168
+ <ul class="pick-list">
169
+ <li><strong>NeuralGuard</strong> - Content moderation</li>
170
+ <li><strong>Guardrails AI</strong> - Output validation</li>
171
+ <li><strong>LangChain Guard</strong> - Prompt protection</li>
172
+ <li><strong>Moderation API</strong> - Hugging Face's solution</li>
173
+ </ul>
174
+ </div>
175
+
176
+ <div class="pick-category">
177
+ <h3>⚡ Optimization</h3>
178
+ <ul class="pick-list">
179
+ <li><strong>GGML</strong> - Model quantization</li>
180
+ <li><strong>Ollama</strong> - Local model serving</li>
181
+ <li><strong>vLLM</strong> - High-performance inference</li>
182
+ <li><strong>TensorRT</strong> - NVIDIA optimization</li>
183
+ </ul>
184
+ </div>
185
+ </div>
186
+ </section>
187
  </main>
188
  </body>
189
  </html>
notebooks/test_notebook.ipynb ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "# `nbgradio` Test Notebook\n",
8
+ "\n",
9
+ "This notebook demonstrates the `nbgradio` library with a simple Gradio app.\n"
10
+ ]
11
+ },
12
+ {
13
+ "cell_type": "code",
14
+ "execution_count": null,
15
+ "metadata": {},
16
+ "outputs": [],
17
+ "source": [
18
+ "#nbgradio name=\"test_notebook\"\n",
19
+ "# nbgradio name=\"greet\"\n",
20
+ "import gradio as gr\n",
21
+ "\n",
22
+ "\n",
23
+ "def greet(name):\n",
24
+ " return f\"Hello {name}!\"\n",
25
+ "\n",
26
+ "\n",
27
+ "demo = gr.Interface(\n",
28
+ " fn=greet, inputs=gr.Textbox(label=\"Your name\"), outputs=gr.Textbox(label=\"Greeting\")\n",
29
+ ")\n",
30
+ "\n",
31
+ "demo.launch()"
32
+ ]
33
+ },
34
+ {
35
+ "cell_type": "markdown",
36
+ "metadata": {},
37
+ "source": [
38
+ "## Another Section\n",
39
+ "\n",
40
+ "The code block above used the `#nbgradio` syntax so that it is launched as a Gradio app. If you don't add that comment block, the `nbgradio` will treat it as \"regular\" code that should just be syntax highlighted:\n"
41
+ ]
42
+ },
43
+ {
44
+ "cell_type": "code",
45
+ "execution_count": null,
46
+ "metadata": {},
47
+ "outputs": [],
48
+ "source": [
49
+ "def calculate_fibonacci(n):\n",
50
+ " \"\"\"Calculate the nth Fibonacci number.\"\"\"\n",
51
+ " if n <= 1:\n",
52
+ " return n\n",
53
+ " return calculate_fibonacci(n - 1) + calculate_fibonacci(n - 2)\n",
54
+ "\n",
55
+ "\n",
56
+ "# Test the function\n",
57
+ "for i in range(10):\n",
58
+ " print(f\"F({i}) = {calculate_fibonacci(i)}\")"
59
+ ]
60
+ }
61
+ ],
62
+ "metadata": {
63
+ "language_info": {
64
+ "name": "python"
65
+ }
66
+ },
67
+ "nbformat": 4,
68
+ "nbformat_minor": 2
69
+ }
notebooks/whisper_api_to_whisper_cpp.ipynb ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "# Whisper API → Whisper.cpp Migration\n",
8
+ "\n",
9
+ "This notebook demonstrates how to migrate from OpenAI's Whisper API to using Whisper.cpp for local speech-to-text processing.\n",
10
+ "\n",
11
+ "## Benefits of Whisper.cpp\n",
12
+ "- **Local processing**: No API calls, complete privacy\n",
13
+ "- **Cost savings**: No per-minute charges\n",
14
+ "- **Offline capability**: Works without internet\n",
15
+ "- **Customization**: Fine-tune for your specific use case\n"
16
+ ]
17
+ },
18
+ {
19
+ "cell_type": "markdown",
20
+ "metadata": {},
21
+ "source": [
22
+ "## Installation\n"
23
+ ]
24
+ },
25
+ {
26
+ "cell_type": "code",
27
+ "execution_count": null,
28
+ "metadata": {},
29
+ "outputs": [],
30
+ "source": [
31
+ "# Install whisper.cpp and dependencies\n",
32
+ "%pip install whisper-cpp-python\n",
33
+ "%pip install librosa soundfile\n"
34
+ ]
35
+ },
36
+ {
37
+ "cell_type": "markdown",
38
+ "metadata": {},
39
+ "source": [
40
+ "## Setup\n"
41
+ ]
42
+ },
43
+ {
44
+ "cell_type": "code",
45
+ "execution_count": null,
46
+ "metadata": {},
47
+ "outputs": [],
48
+ "source": [
49
+ "import whisper_cpp\n",
50
+ "import librosa\n",
51
+ "import soundfile as sf\n",
52
+ "import numpy as np\n",
53
+ "from pathlib import Path\n"
54
+ ]
55
+ },
56
+ {
57
+ "cell_type": "markdown",
58
+ "metadata": {},
59
+ "source": [
60
+ "## Model Loading\n"
61
+ ]
62
+ },
63
+ {
64
+ "cell_type": "code",
65
+ "execution_count": null,
66
+ "metadata": {},
67
+ "outputs": [],
68
+ "source": [
69
+ "# Load Whisper model (downloads automatically on first run)\n",
70
+ "model = whisper_cpp.Whisper.from_pretrained(\"base\")\n",
71
+ "print(\"Model loaded successfully!\")\n"
72
+ ]
73
+ },
74
+ {
75
+ "cell_type": "markdown",
76
+ "metadata": {},
77
+ "source": [
78
+ "## Audio Processing Function\n"
79
+ ]
80
+ },
81
+ {
82
+ "cell_type": "code",
83
+ "execution_count": null,
84
+ "metadata": {},
85
+ "outputs": [],
86
+ "source": [
87
+ "def transcribe_audio(audio_file_path):\n",
88
+ " \"\"\"\n",
89
+ " Transcribe audio file using Whisper.cpp\n",
90
+ " \n",
91
+ " Args:\n",
92
+ " audio_file_path (str): Path to audio file\n",
93
+ " \n",
94
+ " Returns:\n",
95
+ " dict: Transcription result with text and metadata\n",
96
+ " \"\"\"\n",
97
+ " # Load audio file\n",
98
+ " audio, sr = librosa.load(audio_file_path, sr=16000)\n",
99
+ " \n",
100
+ " # Transcribe using Whisper.cpp\n",
101
+ " result = model.transcribe(audio)\n",
102
+ " \n",
103
+ " return {\n",
104
+ " \"text\": result[\"text\"],\n",
105
+ " \"language\": result.get(\"language\", \"auto\"),\n",
106
+ " \"segments\": result.get(\"segments\", [])\n",
107
+ " }\n"
108
+ ]
109
+ },
110
+ {
111
+ "cell_type": "markdown",
112
+ "metadata": {},
113
+ "source": [
114
+ "## Interactive Demo\n"
115
+ ]
116
+ },
117
+ {
118
+ "cell_type": "code",
119
+ "execution_count": null,
120
+ "metadata": {},
121
+ "outputs": [],
122
+ "source": [
123
+ "#nbgradio name=\"whisper_api_to_whisper_cpp\"\n",
124
+ "import gradio as gr\n",
125
+ "\n",
126
+ "def process_audio(audio_file):\n",
127
+ " if audio_file is None:\n",
128
+ " return \"Please upload an audio file.\"\n",
129
+ " \n",
130
+ " try:\n",
131
+ " result = transcribe_audio(audio_file)\n",
132
+ " return f\"**Transcription:**\\\\n{result['text']}\\\\n\\\\n**Language:** {result['language']}\"\n",
133
+ " except Exception as e:\n",
134
+ " return f\"Error processing audio: {str(e)}\"\n",
135
+ "\n",
136
+ "# Create Gradio interface\n",
137
+ "demo = gr.Interface(\n",
138
+ " fn=process_audio,\n",
139
+ " inputs=gr.Audio(type=\"filepath\"),\n",
140
+ " outputs=gr.Markdown(),\n",
141
+ " title=\"Whisper.cpp Speech-to-Text\",\n",
142
+ " description=\"Upload an audio file to transcribe it using Whisper.cpp (local processing)\",\n",
143
+ " examples=[\n",
144
+ " # Add example audio files here\n",
145
+ " ]\n",
146
+ ")\n",
147
+ "\n",
148
+ "demo.launch()\n"
149
+ ]
150
+ },
151
+ {
152
+ "cell_type": "markdown",
153
+ "metadata": {},
154
+ "source": []
155
+ }
156
+ ],
157
+ "metadata": {
158
+ "language_info": {
159
+ "name": "python"
160
+ }
161
+ },
162
+ "nbformat": 4,
163
+ "nbformat_minor": 2
164
+ }
recipes/test_notebook.html ADDED
@@ -0,0 +1,472 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width" />
6
+ <title>Test Notebook - ClosedToOpen</title>
7
+ <link rel="stylesheet" href="../style.css" />
8
+ </head>
9
+ <body>
10
+ <nav class="navbar">
11
+ <div class="nav-content">
12
+ <h1 class="nav-title">ClosedToOpen</h1>
13
+ <p class="nav-subtitle">how to stop relying on proprietary APIs for AI and start living freely</p>
14
+ </div>
15
+ </nav>
16
+
17
+ <main class="main-content">
18
+ <div class="breadcrumb">
19
+ <a href="../index.html">← Back to Migration Guides</a>
20
+ </div>
21
+
22
+ <section class="recipe-header">
23
+ <h1 class="recipe-title">Test Notebook</h1>
24
+ <p class="recipe-description">Migrate from proprietary APIs to open-source alternatives</p>
25
+ <div class="recipe-meta">
26
+ <span class="difficulty">Difficulty: Medium</span>
27
+ <span class="time">Time: 45 minutes</span>
28
+ <span class="category">Category: Migration</span>
29
+ </div>
30
+ </section>
31
+
32
+ <section class="recipe-content">
33
+ <div class="notebook-content">
34
+ <style>
35
+ /* Pygments syntax highlighting - Light theme */
36
+ pre { line-height: 125%; }
37
+ td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
38
+ span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
39
+ td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
40
+ span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
41
+ .highlight .hll { background-color: #ffffcc }
42
+ .highlight { background: #f8f8f8; }
43
+ .highlight .c { color: #3D7B7B; font-style: italic } /* Comment */
44
+ .highlight .err { border: 1px solid #F00 } /* Error */
45
+ .highlight .k { color: #008000; font-weight: bold } /* Keyword */
46
+ .highlight .o { color: #666 } /* Operator */
47
+ .highlight .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */
48
+ .highlight .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */
49
+ .highlight .cp { color: #9C6500 } /* Comment.Preproc */
50
+ .highlight .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */
51
+ .highlight .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */
52
+ .highlight .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */
53
+ .highlight .gd { color: #A00000 } /* Generic.Deleted */
54
+ .highlight .ge { font-style: italic } /* Generic.Emph */
55
+ .highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
56
+ .highlight .gr { color: #E40000 } /* Generic.Error */
57
+ .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
58
+ .highlight .gi { color: #008400 } /* Generic.Inserted */
59
+ .highlight .go { color: #717171 } /* Generic.Output */
60
+ .highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
61
+ .highlight .gs { font-weight: bold } /* Generic.Strong */
62
+ .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
63
+ .highlight .gt { color: #04D } /* Generic.Traceback */
64
+ .highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
65
+ .highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
66
+ .highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
67
+ .highlight .kp { color: #008000 } /* Keyword.Pseudo */
68
+ .highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
69
+ .highlight .kt { color: #B00040 } /* Keyword.Type */
70
+ .highlight .m { color: #666 } /* Literal.Number */
71
+ .highlight .s { color: #BA2121 } /* Literal.String */
72
+ .highlight .na { color: #687822 } /* Name.Attribute */
73
+ .highlight .nb { color: #008000 } /* Name.Builtin */
74
+ .highlight .nc { color: #00F; font-weight: bold } /* Name.Class */
75
+ .highlight .no { color: #800 } /* Name.Constant */
76
+ .highlight .nd { color: #A2F } /* Name.Decorator */
77
+ .highlight .ni { color: #717171; font-weight: bold } /* Name.Entity */
78
+ .highlight .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */
79
+ .highlight .nf { color: #00F } /* Name.Function */
80
+ .highlight .nl { color: #767600 } /* Name.Label */
81
+ .highlight .nn { color: #00F; font-weight: bold } /* Name.Namespace */
82
+ .highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */
83
+ .highlight .nv { color: #19177C } /* Name.Variable */
84
+ .highlight .ow { color: #A2F; font-weight: bold } /* Operator.Word */
85
+ .highlight .w { color: #BBB } /* Text.Whitespace */
86
+ .highlight .mb { color: #666 } /* Literal.Number.Bin */
87
+ .highlight .mf { color: #666 } /* Literal.Number.Float */
88
+ .highlight .mh { color: #666 } /* Literal.Number.Hex */
89
+ .highlight .mi { color: #666 } /* Literal.Number.Integer */
90
+ .highlight .mo { color: #666 } /* Literal.Number.Oct */
91
+ .highlight .sa { color: #BA2121 } /* Literal.String.Affix */
92
+ .highlight .sb { color: #BA2121 } /* Literal.String.Backtick */
93
+ .highlight .sc { color: #BA2121 } /* Literal.String.Char */
94
+ .highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */
95
+ .highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
96
+ .highlight .s2 { color: #BA2121 } /* Literal.String.Double */
97
+ .highlight .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */
98
+ .highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */
99
+ .highlight .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */
100
+ .highlight .sx { color: #008000 } /* Literal.String.Other */
101
+ .highlight .sr { color: #A45A77 } /* Literal.String.Regex */
102
+ .highlight .s1 { color: #BA2121 } /* Literal.String.Single */
103
+ .highlight .ss { color: #19177C } /* Literal.String.Symbol */
104
+ .highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */
105
+ .highlight .fm { color: #00F } /* Name.Function.Magic */
106
+ .highlight .vc { color: #19177C } /* Name.Variable.Class */
107
+ .highlight .vg { color: #19177C } /* Name.Variable.Global */
108
+ .highlight .vi { color: #19177C } /* Name.Variable.Instance */
109
+ .highlight .vm { color: #19177C } /* Name.Variable.Magic */
110
+ .highlight .il { color: #666 } /* Literal.Number.Integer.Long */
111
+
112
+ /* Pygments syntax highlighting - Dark theme */
113
+ @media (prefers-color-scheme: dark) {
114
+ pre { line-height: 125%; }
115
+ td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
116
+ span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
117
+ td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
118
+ span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
119
+ .highlight .hll { background-color: #49483e }
120
+ .highlight { background: #272822; color: #F8F8F2 }
121
+ .highlight .c { color: #959077 } /* Comment */
122
+ .highlight .err { color: #ED007E; background-color: #1E0010 } /* Error */
123
+ .highlight .esc { color: #F8F8F2 } /* Escape */
124
+ .highlight .g { color: #F8F8F2 } /* Generic */
125
+ .highlight .k { color: #66D9EF } /* Keyword */
126
+ .highlight .l { color: #AE81FF } /* Literal */
127
+ .highlight .n { color: #F8F8F2 } /* Name */
128
+ .highlight .o { color: #FF4689 } /* Operator */
129
+ .highlight .x { color: #F8F8F2 } /* Other */
130
+ .highlight .p { color: #F8F8F2 } /* Punctuation */
131
+ .highlight .ch { color: #959077 } /* Comment.Hashbang */
132
+ .highlight .cm { color: #959077 } /* Comment.Multiline */
133
+ .highlight .cp { color: #959077 } /* Comment.Preproc */
134
+ .highlight .cpf { color: #959077 } /* Comment.PreprocFile */
135
+ .highlight .c1 { color: #959077 } /* Comment.Single */
136
+ .highlight .cs { color: #959077 } /* Comment.Special */
137
+ .highlight .gd { color: #FF4689 } /* Generic.Deleted */
138
+ .highlight .ge { color: #F8F8F2; font-style: italic } /* Generic.Emph */
139
+ .highlight .ges { color: #F8F8F2; font-weight: bold; font-style: italic } /* Generic.EmphStrong */
140
+ .highlight .gr { color: #F8F8F2 } /* Generic.Error */
141
+ .highlight .gh { color: #F8F8F2 } /* Generic.Heading */
142
+ .highlight .gi { color: #A6E22E } /* Generic.Inserted */
143
+ .highlight .go { color: #66D9EF } /* Generic.Output */
144
+ .highlight .gp { color: #FF4689; font-weight: bold } /* Generic.Prompt */
145
+ .highlight .gs { color: #F8F8F2; font-weight: bold } /* Generic.Strong */
146
+ .highlight .gu { color: #959077 } /* Generic.Subheading */
147
+ .highlight .gt { color: #F8F8F2 } /* Generic.Traceback */
148
+ .highlight .kc { color: #66D9EF } /* Keyword.Constant */
149
+ .highlight .kd { color: #66D9EF } /* Keyword.Declaration */
150
+ .highlight .kn { color: #FF4689 } /* Keyword.Namespace */
151
+ .highlight .kp { color: #66D9EF } /* Keyword.Pseudo */
152
+ .highlight .kr { color: #66D9EF } /* Keyword.Reserved */
153
+ .highlight .kt { color: #66D9EF } /* Keyword.Type */
154
+ .highlight .ld { color: #E6DB74 } /* Literal.Date */
155
+ .highlight .m { color: #AE81FF } /* Literal.Number */
156
+ .highlight .s { color: #E6DB74 } /* Literal.String */
157
+ .highlight .na { color: #A6E22E } /* Name.Attribute */
158
+ .highlight .nb { color: #F8F8F2 } /* Name.Builtin */
159
+ .highlight .nc { color: #A6E22E } /* Name.Class */
160
+ .highlight .no { color: #66D9EF } /* Name.Constant */
161
+ .highlight .nd { color: #A6E22E } /* Name.Decorator */
162
+ .highlight .ni { color: #F8F8F2 } /* Name.Entity */
163
+ .highlight .ne { color: #A6E22E } /* Name.Exception */
164
+ .highlight .nf { color: #A6E22E } /* Name.Function */
165
+ .highlight .nl { color: #F8F8F2 } /* Name.Label */
166
+ .highlight .nn { color: #F8F8F2 } /* Name.Namespace */
167
+ .highlight .nx { color: #A6E22E } /* Name.Other */
168
+ .highlight .py { color: #F8F8F2 } /* Name.Property */
169
+ .highlight .nt { color: #FF4689 } /* Name.Tag */
170
+ .highlight .nv { color: #F8F8F2 } /* Name.Variable */
171
+ .highlight .ow { color: #FF4689 } /* Operator.Word */
172
+ .highlight .pm { color: #F8F8F2 } /* Punctuation.Marker */
173
+ .highlight .w { color: #F8F8F2 } /* Text.Whitespace */
174
+ .highlight .mb { color: #AE81FF } /* Literal.Number.Bin */
175
+ .highlight .mf { color: #AE81FF } /* Literal.Number.Float */
176
+ .highlight .mh { color: #AE81FF } /* Literal.Number.Hex */
177
+ .highlight .mi { color: #AE81FF } /* Literal.Number.Integer */
178
+ .highlight .mo { color: #AE81FF } /* Literal.Number.Oct */
179
+ .highlight .sa { color: #E6DB74 } /* Literal.String.Affix */
180
+ .highlight .sb { color: #E6DB74 } /* Literal.String.Backtick */
181
+ .highlight .sc { color: #E6DB74 } /* Literal.String.Char */
182
+ .highlight .dl { color: #E6DB74 } /* Literal.String.Delimiter */
183
+ .highlight .sd { color: #E6DB74 } /* Literal.String.Doc */
184
+ .highlight .s2 { color: #E6DB74 } /* Literal.String.Double */
185
+ .highlight .se { color: #AE81FF } /* Literal.String.Escape */
186
+ .highlight .sh { color: #E6DB74 } /* Literal.String.Heredoc */
187
+ .highlight .si { color: #E6DB74 } /* Literal.String.Interpol */
188
+ .highlight .sx { color: #E6DB74 } /* Literal.String.Other */
189
+ .highlight .sr { color: #E6DB74 } /* Literal.String.Regex */
190
+ .highlight .s1 { color: #E6DB74 } /* Literal.String.Single */
191
+ .highlight .ss { color: #E6DB74 } /* Literal.String.Symbol */
192
+ .highlight .bp { color: #F8F8F2 } /* Name.Builtin.Pseudo */
193
+ .highlight .fm { color: #A6E22E } /* Name.Function.Magic */
194
+ .highlight .vc { color: #F8F8F2 } /* Name.Variable.Class */
195
+ .highlight .vg { color: #F8F8F2 } /* Name.Variable.Global */
196
+ .highlight .vi { color: #F8F8F2 } /* Name.Variable.Instance */
197
+ .highlight .vm { color: #F8F8F2 } /* Name.Variable.Magic */
198
+ .highlight .il { color: #AE81FF } /* Literal.Number.Integer.Long */
199
+ }
200
+
201
+ /* Override Pygments background in dark mode to match cell background */
202
+ @media (prefers-color-scheme: dark) {
203
+ .highlight {
204
+ background: #0f0f11 !important;
205
+ }
206
+ }
207
+
208
+
209
+ /* CSS Custom Properties for theming */
210
+ :root {
211
+ --bg-color: #ffffff;
212
+ --text-color: #333333;
213
+ --border-color: #e9ecef;
214
+ --code-bg: #e9ecef;
215
+ --markdown-bg: #f8f9fa;
216
+ --markdown-border: #007acc;
217
+ --cell-bg: #f8f9fa;
218
+ }
219
+
220
+ @media (prefers-color-scheme: dark) {
221
+ :root {
222
+ --bg-color: #1a1a1a;
223
+ --text-color: #e0e0e0;
224
+ --border-color: #404040;
225
+ --code-bg: #0f0f11;
226
+ --markdown-bg: #0f0f11;
227
+ --markdown-border: #4a9eff;
228
+ --cell-bg: #0f0f11;
229
+ }
230
+ }
231
+
232
+ /* nbgradio CSS */
233
+ body {
234
+ background-color: var(--bg-color);
235
+ color: var(--text-color);
236
+ transition: background-color 0.3s ease, color 0.3s ease;
237
+ }
238
+
239
+ .nbgradio-content {
240
+ max-width: 1200px;
241
+ margin: 0 auto;
242
+ padding: 10px;
243
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
244
+ line-height: 1.6;
245
+ }
246
+
247
+ .nbgradio-fragment {
248
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
249
+ line-height: 1.6;
250
+ }
251
+
252
+ .cell {
253
+ margin-bottom: 10px;
254
+ border-radius: 8px;
255
+ overflow: hidden;
256
+ }
257
+
258
+ .cell:first-child {
259
+ margin-top: 20px;
260
+ }
261
+
262
+ .cell-markdown {
263
+ padding: 12px;
264
+ background: var(--markdown-bg);
265
+ border-left: 4px solid var(--markdown-border);
266
+ transition: background-color 0.3s ease;
267
+ }
268
+
269
+ .cell-markdown h1,
270
+ .cell-markdown h2,
271
+ .cell-markdown h3,
272
+ .cell-markdown h4,
273
+ .cell-markdown h5,
274
+ .cell-markdown h6 {
275
+ margin-top: 0;
276
+ color: var(--text-color);
277
+ }
278
+
279
+ .cell-markdown p {
280
+ margin-bottom: 16px;
281
+ color: var(--text-color);
282
+ }
283
+
284
+ .cell-markdown code {
285
+ background: var(--code-bg);
286
+ padding: 2px 6px;
287
+ border-radius: 4px;
288
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
289
+ font-size: 0.9em;
290
+ color: var(--text-color);
291
+ }
292
+
293
+ .cell-markdown pre {
294
+ background: var(--markdown-bg);
295
+ padding: 8px;
296
+ border-radius: 8px;
297
+ overflow-x: auto;
298
+ border: 1px solid var(--border-color);
299
+ }
300
+
301
+ .cell-markdown pre code {
302
+ background: none;
303
+ padding: 0;
304
+ }
305
+
306
+ .cell-code {
307
+ background: var(--cell-bg);
308
+ border: 1px solid var(--border-color);
309
+ transition: background-color 0.3s ease, border-color 0.3s ease;
310
+ }
311
+
312
+ .cell-code pre {
313
+ margin: 0;
314
+ padding: 8px 8px 4px 8px;
315
+ overflow-x: auto;
316
+ }
317
+
318
+ .cell-code code {
319
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
320
+ font-size: 14px;
321
+ line-height: 1.5;
322
+ color: var(--text-color);
323
+ }
324
+
325
+ .cell-gradio {
326
+ margin-bottom: 10px;
327
+ }
328
+
329
+ .gradio-app {
330
+ width: 100%;
331
+ }
332
+
333
+ .gradio-loading-container {
334
+ display: flex;
335
+ align-items: center;
336
+ justify-content: center;
337
+ padding: 40px;
338
+ margin-bottom: 10px;
339
+ background: var(--cell-bg);
340
+ border: 1px solid var(--border-color);
341
+ border-radius: 8px;
342
+ gap: 12px;
343
+ }
344
+
345
+ .gradio-spinner {
346
+ width: 20px;
347
+ height: 20px;
348
+ border: 3px solid #2c2c2c;
349
+ border-top: 3px solid #ff7c00;
350
+ border-radius: 50%;
351
+ animation: gradio-spin 1s linear infinite;
352
+ }
353
+
354
+ .gradio-loading-text {
355
+ color: var(--text-color);
356
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
357
+ font-size: 14px;
358
+ font-weight: 500;
359
+ }
360
+
361
+ @keyframes gradio-spin {
362
+ 0% { transform: rotate(0deg); }
363
+ 100% { transform: rotate(360deg); }
364
+ }
365
+
366
+ @media (prefers-color-scheme: dark) {
367
+ .gradio-spinner {
368
+ border: 3px solid #404040;
369
+ border-top: 3px solid #ff7c00;
370
+ }
371
+ }
372
+
373
+ /* Responsive design */
374
+ @media (max-width: 768px) {
375
+ .nbgradio-content {
376
+ padding: 5px;
377
+ }
378
+
379
+ .cell-markdown,
380
+ .cell-code {
381
+ padding: 8px;
382
+ }
383
+ }
384
+
385
+ </style>
386
+ <script
387
+ type="module"
388
+ src="https://gradio.s3-us-west-2.amazonaws.com/5.49.1/gradio.js"
389
+ ></script>
390
+ <div class="nbgradio-fragment">
391
+ <div class="cell cell-markdown"><h1><code>nbgradio</code> Test Notebook</h1>
392
+ <p>This notebook demonstrates the <code>nbgradio</code> library with a simple Gradio app.</p>
393
+ </div>
394
+
395
+ <div class="cell cell-gradio" id="gradio-cell-test_notebook">
396
+ <div class="gradio-loading-container">
397
+ <div class="gradio-spinner"></div>
398
+ <span class="gradio-loading-text">Loading...</span>
399
+ </div>
400
+ <gradio-app space="abidlabs/test_notebook" class="gradio-app" style="display: none;"></gradio-app>
401
+ <script>
402
+ (function() {
403
+ const gradioApp = document.querySelector('#gradio-cell-test_notebook gradio-app');
404
+ const loadingContainer = document.querySelector('#gradio-cell-test_notebook .gradio-loading-container');
405
+ let isLoaded = false;
406
+ let checkCount = 0;
407
+ const maxChecks = 40; // 20 seconds max
408
+
409
+ function handleLoadComplete() {
410
+ if (!isLoaded) {
411
+ isLoaded = true;
412
+ console.log('Gradio app loaded for test_notebook');
413
+ loadingContainer.style.display = 'none';
414
+ gradioApp.style.display = 'block';
415
+ }
416
+ }
417
+
418
+ // Try multiple event types that Gradio might emit
419
+ const events = ['render', 'loaded', 'ready', 'mount'];
420
+ events.forEach(event => {
421
+ gradioApp.addEventListener(event, handleLoadComplete);
422
+ });
423
+
424
+ // Fallback: Check if the Gradio app has content loaded
425
+ const checkLoaded = () => {
426
+ checkCount++;
427
+
428
+ // Multiple detection methods
429
+ const hasShadowContent = gradioApp.shadowRoot && gradioApp.shadowRoot.children.length > 0;
430
+ const hasVisibleContent = gradioApp.offsetHeight > 100; // App has some height
431
+ const hasIframe = gradioApp.querySelector('iframe');
432
+ const hasGradioContent = gradioApp.innerHTML.includes('gradio') ||
433
+ (gradioApp.shadowRoot &&
434
+ gradioApp.shadowRoot.innerHTML.includes('gradio'));
435
+
436
+ if (hasShadowContent || hasVisibleContent || hasIframe || hasGradioContent) {
437
+ handleLoadComplete();
438
+ } else if (checkCount < maxChecks) {
439
+ setTimeout(checkLoaded, 500);
440
+ } else {
441
+ // Give up after maxChecks attempts and show the app anyway
442
+ handleLoadComplete();
443
+ }
444
+ };
445
+
446
+ // Start checking after a short delay
447
+ setTimeout(checkLoaded, 1000);
448
+ })();
449
+ </script>
450
+ </div>
451
+
452
+ <div class="cell cell-markdown"><h2>Another Section</h2>
453
+ <p>The code block above used the <code>#nbgradio</code> syntax so that it is launched as a Gradio app. If you don't add that comment block, the <code>nbgradio</code> will treat it as &quot;regular&quot; code that should just be syntax highlighted:</p>
454
+ </div>
455
+ <div class="cell cell-code"><pre><code><div class="highlight"><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">calculate_fibonacci</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
456
+ <span class="w"> </span><span class="sd">&quot;&quot;&quot;Calculate the nth Fibonacci number.&quot;&quot;&quot;</span>
457
+ <span class="k">if</span> <span class="n">n</span> <span class="o">&lt;=</span> <span class="mi">1</span><span class="p">:</span>
458
+ <span class="k">return</span> <span class="n">n</span>
459
+ <span class="k">return</span> <span class="n">calculate_fibonacci</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="n">calculate_fibonacci</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">2</span><span class="p">)</span>
460
+
461
+
462
+ <span class="c1"># Test the function</span>
463
+ <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">):</span>
464
+ <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;F(</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s2">) = </span><span class="si">{</span><span class="n">calculate_fibonacci</span><span class="p">(</span><span class="n">i</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
465
+ </code></pre></div>
466
+ </code></pre></div>
467
+ </div>
468
+ </div>
469
+ </section>
470
+ </main>
471
+ </body>
472
+ </html>
recipes/whisper_api_to_whisper_cpp.html ADDED
@@ -0,0 +1,515 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width" />
6
+ <title>Whisper Api To Whisper Cpp - ClosedToOpen</title>
7
+ <link rel="stylesheet" href="../style.css" />
8
+ </head>
9
+ <body>
10
+ <nav class="navbar">
11
+ <div class="nav-content">
12
+ <h1 class="nav-title">ClosedToOpen</h1>
13
+ <p class="nav-subtitle">how to stop relying on proprietary APIs for AI and start living freely</p>
14
+ </div>
15
+ </nav>
16
+
17
+ <main class="main-content">
18
+ <div class="breadcrumb">
19
+ <a href="../index.html">← Back to Migration Guides</a>
20
+ </div>
21
+
22
+ <section class="recipe-header">
23
+ <h1 class="recipe-title">Whisper Api To Whisper Cpp</h1>
24
+ <p class="recipe-description">Migrate from proprietary APIs to open-source alternatives</p>
25
+ <div class="recipe-meta">
26
+ <span class="difficulty">Difficulty: Medium</span>
27
+ <span class="time">Time: 45 minutes</span>
28
+ <span class="category">Category: Migration</span>
29
+ </div>
30
+ </section>
31
+
32
+ <section class="recipe-content">
33
+ <div class="notebook-content">
34
+ <style>
35
+ /* Pygments syntax highlighting - Light theme */
36
+ pre { line-height: 125%; }
37
+ td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
38
+ span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
39
+ td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
40
+ span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
41
+ .highlight .hll { background-color: #ffffcc }
42
+ .highlight { background: #f8f8f8; }
43
+ .highlight .c { color: #3D7B7B; font-style: italic } /* Comment */
44
+ .highlight .err { border: 1px solid #F00 } /* Error */
45
+ .highlight .k { color: #008000; font-weight: bold } /* Keyword */
46
+ .highlight .o { color: #666 } /* Operator */
47
+ .highlight .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */
48
+ .highlight .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */
49
+ .highlight .cp { color: #9C6500 } /* Comment.Preproc */
50
+ .highlight .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */
51
+ .highlight .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */
52
+ .highlight .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */
53
+ .highlight .gd { color: #A00000 } /* Generic.Deleted */
54
+ .highlight .ge { font-style: italic } /* Generic.Emph */
55
+ .highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
56
+ .highlight .gr { color: #E40000 } /* Generic.Error */
57
+ .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
58
+ .highlight .gi { color: #008400 } /* Generic.Inserted */
59
+ .highlight .go { color: #717171 } /* Generic.Output */
60
+ .highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
61
+ .highlight .gs { font-weight: bold } /* Generic.Strong */
62
+ .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
63
+ .highlight .gt { color: #04D } /* Generic.Traceback */
64
+ .highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
65
+ .highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
66
+ .highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
67
+ .highlight .kp { color: #008000 } /* Keyword.Pseudo */
68
+ .highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
69
+ .highlight .kt { color: #B00040 } /* Keyword.Type */
70
+ .highlight .m { color: #666 } /* Literal.Number */
71
+ .highlight .s { color: #BA2121 } /* Literal.String */
72
+ .highlight .na { color: #687822 } /* Name.Attribute */
73
+ .highlight .nb { color: #008000 } /* Name.Builtin */
74
+ .highlight .nc { color: #00F; font-weight: bold } /* Name.Class */
75
+ .highlight .no { color: #800 } /* Name.Constant */
76
+ .highlight .nd { color: #A2F } /* Name.Decorator */
77
+ .highlight .ni { color: #717171; font-weight: bold } /* Name.Entity */
78
+ .highlight .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */
79
+ .highlight .nf { color: #00F } /* Name.Function */
80
+ .highlight .nl { color: #767600 } /* Name.Label */
81
+ .highlight .nn { color: #00F; font-weight: bold } /* Name.Namespace */
82
+ .highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */
83
+ .highlight .nv { color: #19177C } /* Name.Variable */
84
+ .highlight .ow { color: #A2F; font-weight: bold } /* Operator.Word */
85
+ .highlight .w { color: #BBB } /* Text.Whitespace */
86
+ .highlight .mb { color: #666 } /* Literal.Number.Bin */
87
+ .highlight .mf { color: #666 } /* Literal.Number.Float */
88
+ .highlight .mh { color: #666 } /* Literal.Number.Hex */
89
+ .highlight .mi { color: #666 } /* Literal.Number.Integer */
90
+ .highlight .mo { color: #666 } /* Literal.Number.Oct */
91
+ .highlight .sa { color: #BA2121 } /* Literal.String.Affix */
92
+ .highlight .sb { color: #BA2121 } /* Literal.String.Backtick */
93
+ .highlight .sc { color: #BA2121 } /* Literal.String.Char */
94
+ .highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */
95
+ .highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
96
+ .highlight .s2 { color: #BA2121 } /* Literal.String.Double */
97
+ .highlight .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */
98
+ .highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */
99
+ .highlight .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */
100
+ .highlight .sx { color: #008000 } /* Literal.String.Other */
101
+ .highlight .sr { color: #A45A77 } /* Literal.String.Regex */
102
+ .highlight .s1 { color: #BA2121 } /* Literal.String.Single */
103
+ .highlight .ss { color: #19177C } /* Literal.String.Symbol */
104
+ .highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */
105
+ .highlight .fm { color: #00F } /* Name.Function.Magic */
106
+ .highlight .vc { color: #19177C } /* Name.Variable.Class */
107
+ .highlight .vg { color: #19177C } /* Name.Variable.Global */
108
+ .highlight .vi { color: #19177C } /* Name.Variable.Instance */
109
+ .highlight .vm { color: #19177C } /* Name.Variable.Magic */
110
+ .highlight .il { color: #666 } /* Literal.Number.Integer.Long */
111
+
112
+ /* Pygments syntax highlighting - Dark theme */
113
+ @media (prefers-color-scheme: dark) {
114
+ pre { line-height: 125%; }
115
+ td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
116
+ span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
117
+ td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
118
+ span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
119
+ .highlight .hll { background-color: #49483e }
120
+ .highlight { background: #272822; color: #F8F8F2 }
121
+ .highlight .c { color: #959077 } /* Comment */
122
+ .highlight .err { color: #ED007E; background-color: #1E0010 } /* Error */
123
+ .highlight .esc { color: #F8F8F2 } /* Escape */
124
+ .highlight .g { color: #F8F8F2 } /* Generic */
125
+ .highlight .k { color: #66D9EF } /* Keyword */
126
+ .highlight .l { color: #AE81FF } /* Literal */
127
+ .highlight .n { color: #F8F8F2 } /* Name */
128
+ .highlight .o { color: #FF4689 } /* Operator */
129
+ .highlight .x { color: #F8F8F2 } /* Other */
130
+ .highlight .p { color: #F8F8F2 } /* Punctuation */
131
+ .highlight .ch { color: #959077 } /* Comment.Hashbang */
132
+ .highlight .cm { color: #959077 } /* Comment.Multiline */
133
+ .highlight .cp { color: #959077 } /* Comment.Preproc */
134
+ .highlight .cpf { color: #959077 } /* Comment.PreprocFile */
135
+ .highlight .c1 { color: #959077 } /* Comment.Single */
136
+ .highlight .cs { color: #959077 } /* Comment.Special */
137
+ .highlight .gd { color: #FF4689 } /* Generic.Deleted */
138
+ .highlight .ge { color: #F8F8F2; font-style: italic } /* Generic.Emph */
139
+ .highlight .ges { color: #F8F8F2; font-weight: bold; font-style: italic } /* Generic.EmphStrong */
140
+ .highlight .gr { color: #F8F8F2 } /* Generic.Error */
141
+ .highlight .gh { color: #F8F8F2 } /* Generic.Heading */
142
+ .highlight .gi { color: #A6E22E } /* Generic.Inserted */
143
+ .highlight .go { color: #66D9EF } /* Generic.Output */
144
+ .highlight .gp { color: #FF4689; font-weight: bold } /* Generic.Prompt */
145
+ .highlight .gs { color: #F8F8F2; font-weight: bold } /* Generic.Strong */
146
+ .highlight .gu { color: #959077 } /* Generic.Subheading */
147
+ .highlight .gt { color: #F8F8F2 } /* Generic.Traceback */
148
+ .highlight .kc { color: #66D9EF } /* Keyword.Constant */
149
+ .highlight .kd { color: #66D9EF } /* Keyword.Declaration */
150
+ .highlight .kn { color: #FF4689 } /* Keyword.Namespace */
151
+ .highlight .kp { color: #66D9EF } /* Keyword.Pseudo */
152
+ .highlight .kr { color: #66D9EF } /* Keyword.Reserved */
153
+ .highlight .kt { color: #66D9EF } /* Keyword.Type */
154
+ .highlight .ld { color: #E6DB74 } /* Literal.Date */
155
+ .highlight .m { color: #AE81FF } /* Literal.Number */
156
+ .highlight .s { color: #E6DB74 } /* Literal.String */
157
+ .highlight .na { color: #A6E22E } /* Name.Attribute */
158
+ .highlight .nb { color: #F8F8F2 } /* Name.Builtin */
159
+ .highlight .nc { color: #A6E22E } /* Name.Class */
160
+ .highlight .no { color: #66D9EF } /* Name.Constant */
161
+ .highlight .nd { color: #A6E22E } /* Name.Decorator */
162
+ .highlight .ni { color: #F8F8F2 } /* Name.Entity */
163
+ .highlight .ne { color: #A6E22E } /* Name.Exception */
164
+ .highlight .nf { color: #A6E22E } /* Name.Function */
165
+ .highlight .nl { color: #F8F8F2 } /* Name.Label */
166
+ .highlight .nn { color: #F8F8F2 } /* Name.Namespace */
167
+ .highlight .nx { color: #A6E22E } /* Name.Other */
168
+ .highlight .py { color: #F8F8F2 } /* Name.Property */
169
+ .highlight .nt { color: #FF4689 } /* Name.Tag */
170
+ .highlight .nv { color: #F8F8F2 } /* Name.Variable */
171
+ .highlight .ow { color: #FF4689 } /* Operator.Word */
172
+ .highlight .pm { color: #F8F8F2 } /* Punctuation.Marker */
173
+ .highlight .w { color: #F8F8F2 } /* Text.Whitespace */
174
+ .highlight .mb { color: #AE81FF } /* Literal.Number.Bin */
175
+ .highlight .mf { color: #AE81FF } /* Literal.Number.Float */
176
+ .highlight .mh { color: #AE81FF } /* Literal.Number.Hex */
177
+ .highlight .mi { color: #AE81FF } /* Literal.Number.Integer */
178
+ .highlight .mo { color: #AE81FF } /* Literal.Number.Oct */
179
+ .highlight .sa { color: #E6DB74 } /* Literal.String.Affix */
180
+ .highlight .sb { color: #E6DB74 } /* Literal.String.Backtick */
181
+ .highlight .sc { color: #E6DB74 } /* Literal.String.Char */
182
+ .highlight .dl { color: #E6DB74 } /* Literal.String.Delimiter */
183
+ .highlight .sd { color: #E6DB74 } /* Literal.String.Doc */
184
+ .highlight .s2 { color: #E6DB74 } /* Literal.String.Double */
185
+ .highlight .se { color: #AE81FF } /* Literal.String.Escape */
186
+ .highlight .sh { color: #E6DB74 } /* Literal.String.Heredoc */
187
+ .highlight .si { color: #E6DB74 } /* Literal.String.Interpol */
188
+ .highlight .sx { color: #E6DB74 } /* Literal.String.Other */
189
+ .highlight .sr { color: #E6DB74 } /* Literal.String.Regex */
190
+ .highlight .s1 { color: #E6DB74 } /* Literal.String.Single */
191
+ .highlight .ss { color: #E6DB74 } /* Literal.String.Symbol */
192
+ .highlight .bp { color: #F8F8F2 } /* Name.Builtin.Pseudo */
193
+ .highlight .fm { color: #A6E22E } /* Name.Function.Magic */
194
+ .highlight .vc { color: #F8F8F2 } /* Name.Variable.Class */
195
+ .highlight .vg { color: #F8F8F2 } /* Name.Variable.Global */
196
+ .highlight .vi { color: #F8F8F2 } /* Name.Variable.Instance */
197
+ .highlight .vm { color: #F8F8F2 } /* Name.Variable.Magic */
198
+ .highlight .il { color: #AE81FF } /* Literal.Number.Integer.Long */
199
+ }
200
+
201
+ /* Override Pygments background in dark mode to match cell background */
202
+ @media (prefers-color-scheme: dark) {
203
+ .highlight {
204
+ background: #0f0f11 !important;
205
+ }
206
+ }
207
+
208
+
209
+ /* CSS Custom Properties for theming */
210
+ :root {
211
+ --bg-color: #ffffff;
212
+ --text-color: #333333;
213
+ --border-color: #e9ecef;
214
+ --code-bg: #e9ecef;
215
+ --markdown-bg: #f8f9fa;
216
+ --markdown-border: #007acc;
217
+ --cell-bg: #f8f9fa;
218
+ }
219
+
220
+ @media (prefers-color-scheme: dark) {
221
+ :root {
222
+ --bg-color: #1a1a1a;
223
+ --text-color: #e0e0e0;
224
+ --border-color: #404040;
225
+ --code-bg: #0f0f11;
226
+ --markdown-bg: #0f0f11;
227
+ --markdown-border: #4a9eff;
228
+ --cell-bg: #0f0f11;
229
+ }
230
+ }
231
+
232
+ /* nbgradio CSS */
233
+ body {
234
+ background-color: var(--bg-color);
235
+ color: var(--text-color);
236
+ transition: background-color 0.3s ease, color 0.3s ease;
237
+ }
238
+
239
+ .nbgradio-content {
240
+ max-width: 1200px;
241
+ margin: 0 auto;
242
+ padding: 10px;
243
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
244
+ line-height: 1.6;
245
+ }
246
+
247
+ .nbgradio-fragment {
248
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
249
+ line-height: 1.6;
250
+ }
251
+
252
+ .cell {
253
+ margin-bottom: 10px;
254
+ border-radius: 8px;
255
+ overflow: hidden;
256
+ }
257
+
258
+ .cell:first-child {
259
+ margin-top: 20px;
260
+ }
261
+
262
+ .cell-markdown {
263
+ padding: 12px;
264
+ background: var(--markdown-bg);
265
+ border-left: 4px solid var(--markdown-border);
266
+ transition: background-color 0.3s ease;
267
+ }
268
+
269
+ .cell-markdown h1,
270
+ .cell-markdown h2,
271
+ .cell-markdown h3,
272
+ .cell-markdown h4,
273
+ .cell-markdown h5,
274
+ .cell-markdown h6 {
275
+ margin-top: 0;
276
+ color: var(--text-color);
277
+ }
278
+
279
+ .cell-markdown p {
280
+ margin-bottom: 16px;
281
+ color: var(--text-color);
282
+ }
283
+
284
+ .cell-markdown code {
285
+ background: var(--code-bg);
286
+ padding: 2px 6px;
287
+ border-radius: 4px;
288
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
289
+ font-size: 0.9em;
290
+ color: var(--text-color);
291
+ }
292
+
293
+ .cell-markdown pre {
294
+ background: var(--markdown-bg);
295
+ padding: 8px;
296
+ border-radius: 8px;
297
+ overflow-x: auto;
298
+ border: 1px solid var(--border-color);
299
+ }
300
+
301
+ .cell-markdown pre code {
302
+ background: none;
303
+ padding: 0;
304
+ }
305
+
306
+ .cell-code {
307
+ background: var(--cell-bg);
308
+ border: 1px solid var(--border-color);
309
+ transition: background-color 0.3s ease, border-color 0.3s ease;
310
+ }
311
+
312
+ .cell-code pre {
313
+ margin: 0;
314
+ padding: 8px 8px 4px 8px;
315
+ overflow-x: auto;
316
+ }
317
+
318
+ .cell-code code {
319
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
320
+ font-size: 14px;
321
+ line-height: 1.5;
322
+ color: var(--text-color);
323
+ }
324
+
325
+ .cell-gradio {
326
+ margin-bottom: 10px;
327
+ }
328
+
329
+ .gradio-app {
330
+ width: 100%;
331
+ }
332
+
333
+ .gradio-loading-container {
334
+ display: flex;
335
+ align-items: center;
336
+ justify-content: center;
337
+ padding: 40px;
338
+ margin-bottom: 10px;
339
+ background: var(--cell-bg);
340
+ border: 1px solid var(--border-color);
341
+ border-radius: 8px;
342
+ gap: 12px;
343
+ }
344
+
345
+ .gradio-spinner {
346
+ width: 20px;
347
+ height: 20px;
348
+ border: 3px solid #2c2c2c;
349
+ border-top: 3px solid #ff7c00;
350
+ border-radius: 50%;
351
+ animation: gradio-spin 1s linear infinite;
352
+ }
353
+
354
+ .gradio-loading-text {
355
+ color: var(--text-color);
356
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
357
+ font-size: 14px;
358
+ font-weight: 500;
359
+ }
360
+
361
+ @keyframes gradio-spin {
362
+ 0% { transform: rotate(0deg); }
363
+ 100% { transform: rotate(360deg); }
364
+ }
365
+
366
+ @media (prefers-color-scheme: dark) {
367
+ .gradio-spinner {
368
+ border: 3px solid #404040;
369
+ border-top: 3px solid #ff7c00;
370
+ }
371
+ }
372
+
373
+ /* Responsive design */
374
+ @media (max-width: 768px) {
375
+ .nbgradio-content {
376
+ padding: 5px;
377
+ }
378
+
379
+ .cell-markdown,
380
+ .cell-code {
381
+ padding: 8px;
382
+ }
383
+ }
384
+
385
+ </style>
386
+ <script
387
+ type="module"
388
+ src="https://gradio.s3-us-west-2.amazonaws.com/5.49.1/gradio.js"
389
+ ></script>
390
+ <div class="nbgradio-fragment">
391
+ <div class="cell cell-markdown"><h1>Whisper API → Whisper.cpp Migration</h1>
392
+ <p>This notebook demonstrates how to migrate from OpenAI's Whisper API to using Whisper.cpp for local speech-to-text processing.</p>
393
+ <h2>Benefits of Whisper.cpp</h2>
394
+ <ul>
395
+ <li><strong>Local processing</strong>: No API calls, complete privacy</li>
396
+ <li><strong>Cost savings</strong>: No per-minute charges</li>
397
+ <li><strong>Offline capability</strong>: Works without internet</li>
398
+ <li><strong>Customization</strong>: Fine-tune for your specific use case</li>
399
+ </ul>
400
+ </div>
401
+ <div class="cell cell-markdown"><h2>Installation</h2>
402
+ </div>
403
+ <div class="cell cell-code"><pre><code><div class="highlight"><pre><span></span><code><span class="c1"># Install whisper.cpp and dependencies</span>
404
+ <span class="o">%</span><span class="n">pip</span> <span class="n">install</span> <span class="n">whisper</span><span class="o">-</span><span class="n">cpp</span><span class="o">-</span><span class="n">python</span>
405
+ <span class="o">%</span><span class="n">pip</span> <span class="n">install</span> <span class="n">librosa</span> <span class="n">soundfile</span>
406
+ </code></pre></div>
407
+ </code></pre></div>
408
+ <div class="cell cell-markdown"><h2>Setup</h2>
409
+ </div>
410
+ <div class="cell cell-code"><pre><code><div class="highlight"><pre><span></span><code><span class="kn">import</span><span class="w"> </span><span class="nn">whisper_cpp</span>
411
+ <span class="kn">import</span><span class="w"> </span><span class="nn">librosa</span>
412
+ <span class="kn">import</span><span class="w"> </span><span class="nn">soundfile</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="nn">sf</span>
413
+ <span class="kn">import</span><span class="w"> </span><span class="nn">numpy</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="nn">np</span>
414
+ <span class="kn">from</span><span class="w"> </span><span class="nn">pathlib</span><span class="w"> </span><span class="kn">import</span> <span class="n">Path</span>
415
+ </code></pre></div>
416
+ </code></pre></div>
417
+ <div class="cell cell-markdown"><h2>Model Loading</h2>
418
+ </div>
419
+ <div class="cell cell-code"><pre><code><div class="highlight"><pre><span></span><code><span class="c1"># Load Whisper model (downloads automatically on first run)</span>
420
+ <span class="n">model</span> <span class="o">=</span> <span class="n">whisper_cpp</span><span class="o">.</span><span class="n">Whisper</span><span class="o">.</span><span class="n">from_pretrained</span><span class="p">(</span><span class="s2">&quot;base&quot;</span><span class="p">)</span>
421
+ <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Model loaded successfully!&quot;</span><span class="p">)</span>
422
+ </code></pre></div>
423
+ </code></pre></div>
424
+ <div class="cell cell-markdown"><h2>Audio Processing Function</h2>
425
+ </div>
426
+ <div class="cell cell-code"><pre><code><div class="highlight"><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">transcribe_audio</span><span class="p">(</span><span class="n">audio_file_path</span><span class="p">):</span>
427
+ <span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
428
+ <span class="sd"> Transcribe audio file using Whisper.cpp</span>
429
+ <span class="sd"> </span>
430
+ <span class="sd"> Args:</span>
431
+ <span class="sd"> audio_file_path (str): Path to audio file</span>
432
+ <span class="sd"> </span>
433
+ <span class="sd"> Returns:</span>
434
+ <span class="sd"> dict: Transcription result with text and metadata</span>
435
+ <span class="sd"> &quot;&quot;&quot;</span>
436
+ <span class="c1"># Load audio file</span>
437
+ <span class="n">audio</span><span class="p">,</span> <span class="n">sr</span> <span class="o">=</span> <span class="n">librosa</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="n">audio_file_path</span><span class="p">,</span> <span class="n">sr</span><span class="o">=</span><span class="mi">16000</span><span class="p">)</span>
438
+
439
+ <span class="c1"># Transcribe using Whisper.cpp</span>
440
+ <span class="n">result</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">transcribe</span><span class="p">(</span><span class="n">audio</span><span class="p">)</span>
441
+
442
+ <span class="k">return</span> <span class="p">{</span>
443
+ <span class="s2">&quot;text&quot;</span><span class="p">:</span> <span class="n">result</span><span class="p">[</span><span class="s2">&quot;text&quot;</span><span class="p">],</span>
444
+ <span class="s2">&quot;language&quot;</span><span class="p">:</span> <span class="n">result</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;language&quot;</span><span class="p">,</span> <span class="s2">&quot;auto&quot;</span><span class="p">),</span>
445
+ <span class="s2">&quot;segments&quot;</span><span class="p">:</span> <span class="n">result</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;segments&quot;</span><span class="p">,</span> <span class="p">[])</span>
446
+ <span class="p">}</span>
447
+ </code></pre></div>
448
+ </code></pre></div>
449
+ <div class="cell cell-markdown"><h2>Interactive Demo</h2>
450
+ </div>
451
+
452
+ <div class="cell cell-gradio" id="gradio-cell-whisper_api_to_whisper_cpp">
453
+ <div class="gradio-loading-container">
454
+ <div class="gradio-spinner"></div>
455
+ <span class="gradio-loading-text">Loading...</span>
456
+ </div>
457
+ <gradio-app space="abidlabs/whisper_api_to_whisper_cpp" class="gradio-app" style="display: none;"></gradio-app>
458
+ <script>
459
+ (function() {
460
+ const gradioApp = document.querySelector('#gradio-cell-whisper_api_to_whisper_cpp gradio-app');
461
+ const loadingContainer = document.querySelector('#gradio-cell-whisper_api_to_whisper_cpp .gradio-loading-container');
462
+ let isLoaded = false;
463
+ let checkCount = 0;
464
+ const maxChecks = 40; // 20 seconds max
465
+
466
+ function handleLoadComplete() {
467
+ if (!isLoaded) {
468
+ isLoaded = true;
469
+ console.log('Gradio app loaded for whisper_api_to_whisper_cpp');
470
+ loadingContainer.style.display = 'none';
471
+ gradioApp.style.display = 'block';
472
+ }
473
+ }
474
+
475
+ // Try multiple event types that Gradio might emit
476
+ const events = ['render', 'loaded', 'ready', 'mount'];
477
+ events.forEach(event => {
478
+ gradioApp.addEventListener(event, handleLoadComplete);
479
+ });
480
+
481
+ // Fallback: Check if the Gradio app has content loaded
482
+ const checkLoaded = () => {
483
+ checkCount++;
484
+
485
+ // Multiple detection methods
486
+ const hasShadowContent = gradioApp.shadowRoot && gradioApp.shadowRoot.children.length > 0;
487
+ const hasVisibleContent = gradioApp.offsetHeight > 100; // App has some height
488
+ const hasIframe = gradioApp.querySelector('iframe');
489
+ const hasGradioContent = gradioApp.innerHTML.includes('gradio') ||
490
+ (gradioApp.shadowRoot &&
491
+ gradioApp.shadowRoot.innerHTML.includes('gradio'));
492
+
493
+ if (hasShadowContent || hasVisibleContent || hasIframe || hasGradioContent) {
494
+ handleLoadComplete();
495
+ } else if (checkCount < maxChecks) {
496
+ setTimeout(checkLoaded, 500);
497
+ } else {
498
+ // Give up after maxChecks attempts and show the app anyway
499
+ handleLoadComplete();
500
+ }
501
+ };
502
+
503
+ // Start checking after a short delay
504
+ setTimeout(checkLoaded, 1000);
505
+ })();
506
+ </script>
507
+ </div>
508
+
509
+ <div class="cell cell-markdown"></div>
510
+ </div>
511
+ </div>
512
+ </section>
513
+ </main>
514
+ </body>
515
+ </html>
style.css CHANGED
@@ -328,12 +328,12 @@ body {
328
  transform: translateY(-1px);
329
  }
330
 
331
- .topic-header {
332
  margin-bottom: 4rem;
333
  text-align: center;
334
  }
335
 
336
- .topic-title {
337
  font-size: 3.5rem;
338
  font-weight: 700;
339
  margin-bottom: 1.5rem;
@@ -342,13 +342,401 @@ body {
342
  text-shadow: 0 2px 4px rgba(0,0,0,0.3);
343
  }
344
 
345
- .topic-description {
346
  font-size: 1.3rem;
347
  color: #b0b0b0;
348
  max-width: 700px;
349
  font-weight: 400;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
350
  margin: 0 auto;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
351
  line-height: 1.6;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
352
  }
353
 
354
  .recipes-section {
@@ -479,4 +867,36 @@ body {
479
  padding: 1.5rem;
480
  min-height: auto;
481
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
482
  }
 
328
  transform: translateY(-1px);
329
  }
330
 
331
+ .recipe-header {
332
  margin-bottom: 4rem;
333
  text-align: center;
334
  }
335
 
336
+ .recipe-title {
337
  font-size: 3.5rem;
338
  font-weight: 700;
339
  margin-bottom: 1.5rem;
 
342
  text-shadow: 0 2px 4px rgba(0,0,0,0.3);
343
  }
344
 
345
+ .recipe-description {
346
  font-size: 1.3rem;
347
  color: #b0b0b0;
348
  max-width: 700px;
349
  font-weight: 400;
350
+ margin: 0 auto 2rem auto;
351
+ line-height: 1.6;
352
+ }
353
+
354
+ .recipe-meta {
355
+ display: flex;
356
+ justify-content: center;
357
+ gap: 2rem;
358
+ flex-wrap: wrap;
359
+ }
360
+
361
+ .recipe-meta span {
362
+ background: rgba(255, 255, 255, 0.1);
363
+ padding: 0.5rem 1rem;
364
+ border-radius: 20px;
365
+ font-size: 0.9rem;
366
+ font-weight: 500;
367
+ color: #d0d0d0;
368
+ border: 1px solid rgba(255, 255, 255, 0.1);
369
+ }
370
+
371
+ .recipe-content {
372
+ max-width: 1000px;
373
+ margin: 0 auto;
374
+ }
375
+
376
+ .content-grid {
377
+ display: grid;
378
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
379
+ gap: 2rem;
380
+ margin-bottom: 3rem;
381
+ }
382
+
383
+ .content-card {
384
+ background: linear-gradient(135deg, rgba(255,255,255,0.05) 0%, rgba(255,255,255,0.02) 100%);
385
+ border: 1px solid rgba(255, 255, 255, 0.1);
386
+ border-radius: 16px;
387
+ padding: 2rem;
388
+ backdrop-filter: blur(10px);
389
+ }
390
+
391
+ .content-card h2 {
392
+ font-size: 1.4rem;
393
+ font-weight: 600;
394
+ margin-bottom: 1rem;
395
+ color: white;
396
+ }
397
+
398
+ .content-card ul {
399
+ color: #b0b0b0;
400
+ line-height: 1.6;
401
+ padding-left: 1.5rem;
402
+ }
403
+
404
+ .content-card li {
405
+ margin-bottom: 0.5rem;
406
+ }
407
+
408
+ .demo-section {
409
+ margin-bottom: 3rem;
410
+ }
411
+
412
+ .demo-section h2 {
413
+ font-size: 2rem;
414
+ font-weight: 600;
415
+ margin-bottom: 1.5rem;
416
+ color: white;
417
+ text-align: center;
418
+ }
419
+
420
+ .demo-container {
421
+ background: rgba(255, 255, 255, 0.05);
422
+ border: 1px solid rgba(255, 255, 255, 0.1);
423
+ border-radius: 16px;
424
+ padding: 1rem;
425
+ backdrop-filter: blur(10px);
426
+ }
427
+
428
+ .code-section {
429
+ margin-bottom: 3rem;
430
+ }
431
+
432
+ .code-section h2 {
433
+ font-size: 2rem;
434
+ font-weight: 600;
435
+ margin-bottom: 1.5rem;
436
+ color: white;
437
+ }
438
+
439
+ .code-block {
440
+ margin-bottom: 2rem;
441
+ }
442
+
443
+ .code-block h3 {
444
+ font-size: 1.2rem;
445
+ font-weight: 600;
446
+ margin-bottom: 1rem;
447
+ color: #d0d0d0;
448
+ }
449
+
450
+ .code-block pre {
451
+ background: rgba(0, 0, 0, 0.3);
452
+ border: 1px solid rgba(255, 255, 255, 0.1);
453
+ border-radius: 8px;
454
+ padding: 1.5rem;
455
+ overflow-x: auto;
456
+ }
457
+
458
+ .code-block code {
459
+ color: #e0e0e0;
460
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
461
+ font-size: 0.9rem;
462
+ line-height: 1.5;
463
+ }
464
+
465
+ .steps-section {
466
+ margin-bottom: 3rem;
467
+ }
468
+
469
+ .steps-section h2 {
470
+ font-size: 2rem;
471
+ font-weight: 600;
472
+ margin-bottom: 1.5rem;
473
+ color: white;
474
+ }
475
+
476
+ .steps-list {
477
+ list-style: none;
478
+ padding: 0;
479
+ }
480
+
481
+ .steps-list li {
482
+ background: linear-gradient(135deg, rgba(255,255,255,0.05) 0%, rgba(255,255,255,0.02) 100%);
483
+ border: 1px solid rgba(255, 255, 255, 0.1);
484
+ border-radius: 12px;
485
+ padding: 1.5rem;
486
+ margin-bottom: 1rem;
487
+ backdrop-filter: blur(10px);
488
+ }
489
+
490
+ .steps-list h3 {
491
+ font-size: 1.2rem;
492
+ font-weight: 600;
493
+ margin-bottom: 0.5rem;
494
+ color: white;
495
+ }
496
+
497
+ .steps-list p {
498
+ color: #b0b0b0;
499
+ margin-bottom: 0.5rem;
500
+ }
501
+
502
+ .steps-list code {
503
+ background: rgba(0, 0, 0, 0.3);
504
+ padding: 0.3rem 0.6rem;
505
+ border-radius: 4px;
506
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
507
+ font-size: 0.85rem;
508
+ color: #e0e0e0;
509
+ }
510
+
511
+ .resources-section {
512
+ margin-bottom: 3rem;
513
+ }
514
+
515
+ .resources-section h2 {
516
+ font-size: 2rem;
517
+ font-weight: 600;
518
+ margin-bottom: 1.5rem;
519
+ color: white;
520
+ }
521
+
522
+ .resources-grid {
523
+ display: grid;
524
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
525
+ gap: 1rem;
526
+ }
527
+
528
+ .resource-link {
529
+ display: block;
530
+ background: linear-gradient(135deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0.05) 100%);
531
+ border: 1px solid rgba(255, 255, 255, 0.2);
532
+ border-radius: 12px;
533
+ padding: 1.5rem;
534
+ text-decoration: none;
535
+ color: #d0d0d0;
536
+ font-weight: 500;
537
+ text-align: center;
538
+ transition: all 0.2s ease;
539
+ }
540
+
541
+ .resource-link:hover {
542
+ background: linear-gradient(135deg, rgba(255,255,255,0.15) 0%, rgba(255,255,255,0.1) 100%);
543
+ border-color: rgba(255, 255, 255, 0.3);
544
+ color: white;
545
+ transform: translateY(-2px);
546
+ }
547
+
548
+ .picks-section {
549
+ margin-top: 4rem;
550
+ }
551
+
552
+ .picks-subtitle {
553
+ font-size: 1.1rem;
554
+ color: #b0b0b0;
555
+ text-align: center;
556
+ margin-bottom: 3rem;
557
+ max-width: 600px;
558
+ margin-left: auto;
559
+ margin-right: auto;
560
+ }
561
+
562
+ .picks-grid {
563
+ display: grid;
564
+ grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
565
+ gap: 2rem;
566
+ max-width: 1200px;
567
  margin: 0 auto;
568
+ }
569
+
570
+ .pick-category {
571
+ background: linear-gradient(135deg, rgba(255,255,255,0.05) 0%, rgba(255,255,255,0.02) 100%);
572
+ border: 1px solid rgba(255, 255, 255, 0.1);
573
+ border-radius: 16px;
574
+ padding: 2rem;
575
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
576
+ position: relative;
577
+ overflow: hidden;
578
+ backdrop-filter: blur(10px);
579
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
580
+ }
581
+
582
+ .pick-category::before {
583
+ content: '';
584
+ position: absolute;
585
+ top: 0;
586
+ left: 0;
587
+ right: 0;
588
+ bottom: 0;
589
+ background: linear-gradient(135deg, rgba(255,255,255,0.05) 0%, rgba(255,255,255,0.02) 100%);
590
+ opacity: 0;
591
+ transition: opacity 0.3s ease;
592
+ }
593
+
594
+ .pick-category:hover {
595
+ transform: translateY(-2px);
596
+ box-shadow: 0 8px 30px rgba(0, 0, 0, 0.15);
597
+ border-color: rgba(255, 255, 255, 0.2);
598
+ }
599
+
600
+ .pick-category:hover::before {
601
+ opacity: 1;
602
+ }
603
+
604
+ .pick-category h3 {
605
+ font-size: 1.4rem;
606
+ font-weight: 600;
607
+ margin-bottom: 1.5rem;
608
+ color: white;
609
+ letter-spacing: -0.01em;
610
+ z-index: 2;
611
+ position: relative;
612
+ }
613
+
614
+ .pick-list {
615
+ list-style: none;
616
+ padding: 0;
617
+ margin: 0;
618
+ z-index: 2;
619
+ position: relative;
620
+ }
621
+
622
+ .pick-list li {
623
+ color: #b0b0b0;
624
+ font-size: 0.95rem;
625
  line-height: 1.6;
626
+ margin-bottom: 0.8rem;
627
+ padding-left: 0;
628
+ }
629
+
630
+ .notebook-content {
631
+ max-width: 1000px;
632
+ margin: 0 auto;
633
+ }
634
+
635
+ .notebook-content h1,
636
+ .notebook-content h2,
637
+ .notebook-content h3,
638
+ .notebook-content h4,
639
+ .notebook-content h5,
640
+ .notebook-content h6 {
641
+ color: white;
642
+ margin-top: 2rem;
643
+ margin-bottom: 1rem;
644
+ font-weight: 600;
645
+ }
646
+
647
+ .notebook-content h1 {
648
+ font-size: 2.5rem;
649
+ letter-spacing: -0.02em;
650
+ }
651
+
652
+ .notebook-content h2 {
653
+ font-size: 2rem;
654
+ letter-spacing: -0.01em;
655
+ }
656
+
657
+ .notebook-content h3 {
658
+ font-size: 1.5rem;
659
+ }
660
+
661
+ .notebook-content p {
662
+ color: #b0b0b0;
663
+ line-height: 1.6;
664
+ margin-bottom: 1rem;
665
+ }
666
+
667
+ .notebook-content ul,
668
+ .notebook-content ol {
669
+ color: #b0b0b0;
670
+ margin-bottom: 1rem;
671
+ padding-left: 2rem;
672
+ }
673
+
674
+ .notebook-content li {
675
+ margin-bottom: 0.5rem;
676
+ }
677
+
678
+ .notebook-content code {
679
+ background: rgba(0, 0, 0, 0.3);
680
+ padding: 0.2rem 0.4rem;
681
+ border-radius: 4px;
682
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
683
+ font-size: 0.9rem;
684
+ color: #e0e0e0;
685
+ }
686
+
687
+ .notebook-content pre {
688
+ background: rgba(0, 0, 0, 0.3);
689
+ border: 1px solid rgba(255, 255, 255, 0.1);
690
+ border-radius: 8px;
691
+ padding: 1.5rem;
692
+ overflow-x: auto;
693
+ margin-bottom: 1.5rem;
694
+ }
695
+
696
+ .notebook-content pre code {
697
+ background: none;
698
+ padding: 0;
699
+ border-radius: 0;
700
+ font-size: 0.9rem;
701
+ line-height: 1.5;
702
+ }
703
+
704
+ .notebook-content blockquote {
705
+ border-left: 4px solid rgba(255, 255, 255, 0.2);
706
+ padding-left: 1rem;
707
+ margin: 1.5rem 0;
708
+ color: #d0d0d0;
709
+ font-style: italic;
710
+ }
711
+
712
+ .notebook-content .gradio-app {
713
+ margin: 2rem 0;
714
+ border-radius: 12px;
715
+ overflow: hidden;
716
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
717
+ }
718
+
719
+ .notebook-content table {
720
+ width: 100%;
721
+ border-collapse: collapse;
722
+ margin: 1.5rem 0;
723
+ }
724
+
725
+ .notebook-content th,
726
+ .notebook-content td {
727
+ border: 1px solid rgba(255, 255, 255, 0.1);
728
+ padding: 0.8rem;
729
+ text-align: left;
730
+ }
731
+
732
+ .notebook-content th {
733
+ background: rgba(255, 255, 255, 0.05);
734
+ color: white;
735
+ font-weight: 600;
736
+ }
737
+
738
+ .notebook-content td {
739
+ color: #b0b0b0;
740
  }
741
 
742
  .recipes-section {
 
867
  padding: 1.5rem;
868
  min-height: auto;
869
  }
870
+
871
+ .recipe-title {
872
+ font-size: 2.5rem;
873
+ }
874
+
875
+ .content-grid {
876
+ grid-template-columns: 1fr;
877
+ gap: 1.5rem;
878
+ }
879
+
880
+ .content-card {
881
+ padding: 1.5rem;
882
+ }
883
+
884
+ .resources-grid {
885
+ grid-template-columns: 1fr;
886
+ gap: 1rem;
887
+ }
888
+
889
+ .recipe-meta {
890
+ flex-direction: column;
891
+ gap: 1rem;
892
+ }
893
+
894
+ .picks-grid {
895
+ grid-template-columns: 1fr;
896
+ gap: 1.5rem;
897
+ }
898
+
899
+ .pick-category {
900
+ padding: 1.5rem;
901
+ }
902
  }