ysharma HF Staff commited on
Commit
e0dad8d
·
verified ·
1 Parent(s): 45dff0a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +110 -269
app.py CHANGED
@@ -5,8 +5,6 @@ from huggingface_hub import login
5
  from sentence_transformers import SentenceTransformer, util
6
 
7
  # --- CONFIGURATION ---
8
- # Centralized place for all settings and constants.
9
-
10
  class Config:
11
  """Configuration settings for the application."""
12
  EMBEDDING_MODEL_ID = "google/embeddinggemma-300M"
@@ -14,10 +12,7 @@ class Config:
14
  TOP_K = 5
15
  HF_TOKEN = os.getenv('HF_TOKEN')
16
 
17
-
18
  # --- FONT DATA ---
19
- # Comprehensive font dataset with descriptions for mood matching.
20
-
21
  FONT_DATA = [
22
  {
23
  "name": "Playfair Display",
@@ -79,144 +74,9 @@ FONT_DATA = [
79
  "google_fonts_url": "https://fonts.googleapis.com/css2?family=Pacifico&display=swap",
80
  "description": "Surfing, California, retro, casual script font with beach vibes and laid-back summer feeling"
81
  },
82
- {
83
- "name": "Source Code Pro",
84
- "family": "monospace",
85
- "google_fonts_url": "https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@300;400;600&display=swap",
86
- "description": "Technical, programming, coding, monospaced font designed for developers and technical documentation"
87
- },
88
- {
89
- "name": "Merriweather",
90
- "family": "serif",
91
- "google_fonts_url": "https://fonts.googleapis.com/css2?family=Merriweather:wght@300;400;700&display=swap",
92
- "description": "Traditional, readable, pleasant serif designed for comfortable reading on screens and in print"
93
- },
94
- {
95
- "name": "Abril Fatface",
96
- "family": "serif",
97
- "google_fonts_url": "https://fonts.googleapis.com/css2?family=Abril+Fatface&display=swap",
98
- "description": "Bold, dramatic, high-contrast display serif inspired by French and Italian typography, perfect for headlines"
99
- },
100
- {
101
- "name": "Great Vibes",
102
- "family": "script",
103
- "google_fonts_url": "https://fonts.googleapis.com/css2?family=Great+Vibes&display=swap",
104
- "description": "Elegant, formal, calligraphic script with sophisticated curves, ideal for luxury and premium branding"
105
- },
106
- {
107
- "name": "Raleway",
108
- "family": "sans-serif",
109
- "google_fonts_url": "https://fonts.googleapis.com/css2?family=Raleway:wght@300;400;600&display=swap",
110
- "description": "Sophisticated, thin, elegant sans-serif with distinctive 'W', perfect for fashion and high-end design"
111
- },
112
- {
113
- "name": "Fredoka One",
114
- "family": "display",
115
- "google_fonts_url": "https://fonts.googleapis.com/css2?family=Fredoka+One&display=swap",
116
- "description": "Friendly, rounded, playful display font perfect for children's content, toys, and fun applications"
117
- },
118
- {
119
- "name": "Libre Baskerville",
120
- "family": "serif",
121
- "google_fonts_url": "https://fonts.googleapis.com/css2?family=Libre+Baskerville:wght@400;700&display=swap",
122
- "description": "Classic, traditional, scholarly serif based on American Type Founder's Baskerville, perfect for academic texts"
123
- },
124
- {
125
- "name": "Poppins",
126
- "family": "sans-serif",
127
- "google_fonts_url": "https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap",
128
- "description": "Geometric, modern, friendly sans-serif with circular forms, popular for contemporary web design"
129
- },
130
- {
131
- "name": "Lobster",
132
- "family": "script",
133
- "google_fonts_url": "https://fonts.googleapis.com/css2?family=Lobster&display=swap",
134
- "description": "Bold, retro, vintage script font with a 1950s diner feel, perfect for nostalgic and Americana designs"
135
- },
136
- {
137
- "name": "Open Sans",
138
- "family": "sans-serif",
139
- "google_fonts_url": "https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;600&display=swap",
140
- "description": "Neutral, friendly, optimistic sans-serif designed for legibility across interfaces, print, and web"
141
- },
142
- {
143
- "name": "Shadows Into Light",
144
- "family": "handwriting",
145
- "google_fonts_url": "https://fonts.googleapis.com/css2?family=Shadows+Into+Light&display=swap",
146
- "description": "Casual, handwritten, personal font that feels like natural handwriting with a marker or pen"
147
- },
148
- {
149
- "name": "Creepster",
150
- "family": "display",
151
- "google_fonts_url": "https://fonts.googleapis.com/css2?family=Creepster&display=swap",
152
- "description": "Horror, scary, Halloween, gothic font with dripping effect, perfect for spooky and thriller themes"
153
- },
154
- {
155
- "name": "Righteous",
156
- "family": "display",
157
- "google_fonts_url": "https://fonts.googleapis.com/css2?family=Righteous&display=swap",
158
- "description": "Futuristic, sci-fi, technology, bold display font with unique character shapes for modern designs"
159
- },
160
- {
161
- "name": "Satisfy",
162
- "family": "script",
163
- "google_fonts_url": "https://fonts.googleapis.com/css2?family=Satisfy&display=swap",
164
- "description": "Casual, relaxed, handwritten script with natural flow, perfect for personal and informal communications"
165
- },
166
- {
167
- "name": "Anton",
168
- "family": "sans-serif",
169
- "google_fonts_url": "https://fonts.googleapis.com/css2?family=Anton&display=swap",
170
- "description": "Bold, condensed, impactful sans-serif perfect for headlines, posters, and attention-grabbing text"
171
- },
172
- {
173
- "name": "Courgette",
174
- "family": "script",
175
- "google_fonts_url": "https://fonts.googleapis.com/css2?family=Courgette&display=swap",
176
- "description": "French, bistro, café, elegant script font with continental European charm and sophistication"
177
- },
178
- {
179
- "name": "Indie Flower",
180
- "family": "handwriting",
181
- "google_fonts_url": "https://fonts.googleapis.com/css2?family=Indie+Flower&display=swap",
182
- "description": "Indie, hipster, handwritten font with quirky personality, perfect for creative and artistic projects"
183
- },
184
- {
185
- "name": "PT Serif",
186
- "family": "serif",
187
- "google_fonts_url": "https://fonts.googleapis.com/css2?family=PT+Serif:wght@400;700&display=swap",
188
- "description": "Russian, Cyrillic, transitional serif with excellent readability for both Latin and Cyrillic scripts"
189
- },
190
- {
191
- "name": "Questrial",
192
- "family": "sans-serif",
193
- "google_fonts_url": "https://fonts.googleapis.com/css2?family=Questrial&display=swap",
194
- "description": "Simple, clean, minimal sans-serif with subtle quirks, perfect for modern and understated designs"
195
- },
196
- {
197
- "name": "Bangers",
198
- "family": "display",
199
- "google_fonts_url": "https://fonts.googleapis.com/css2?family=Bangers&display=swap",
200
- "description": "Comic book, superhero, pop art font inspired by mid-20th century comic books and advertisements"
201
- },
202
- {
203
- "name": "Sacramento",
204
- "family": "script",
205
- "google_fonts_url": "https://fonts.googleapis.com/css2?family=Sacramento&display=swap",
206
- "description": "Monoline, cursive script with vintage charm, perfect for elegant and sophisticated branding"
207
- },
208
- {
209
- "name": "Bitter",
210
- "family": "serif",
211
- "google_fonts_url": "https://fonts.googleapis.com/css2?family=Bitter:wght@400;700&display=swap",
212
- "description": "Contemporary, slab serif with slight contrast, designed for comfortable reading in long texts"
213
- }
214
  ]
215
 
216
-
217
  # --- CORE LOGIC ---
218
- # Encapsulated in a class to manage state (model, embeddings) cleanly.
219
-
220
  class FontMoodGenerator:
221
  """Handles model loading, embedding generation, and font palette creation."""
222
 
@@ -235,7 +95,6 @@ class FontMoodGenerator:
235
  login(token=self.config.HF_TOKEN)
236
  else:
237
  print("HF_TOKEN not found. Proceeding without login.")
238
- print("Note: This may fail if the model is gated.")
239
 
240
  def _load_model(self) -> SentenceTransformer:
241
  """Loads the Sentence Transformer model."""
@@ -265,7 +124,6 @@ class FontMoodGenerator:
265
  if not top_hits:
266
  return "<p>Could not generate a font palette. Please try another mood.</p>"
267
 
268
- # Sample texts for different font types
269
  sample_texts = [
270
  "The Quick Brown Fox Jumps Over The Lazy Dog",
271
  "Sphinx of black quartz, judge my vow",
@@ -299,11 +157,27 @@ class FontMoodGenerator:
299
  """
300
  return f"<div class='font-palette-container'>{cards_html}</div>"
301
 
302
- def _create_font_imports_css(self, top_hits: list[dict[str, any]]) -> str:
303
- """Generates CSS imports for the selected fonts."""
304
- if not top_hits:
305
- return ""
 
 
 
 
 
 
 
 
306
 
 
 
 
 
 
 
 
 
307
  imports = []
308
  seen_urls = set()
309
 
@@ -315,18 +189,50 @@ class FontMoodGenerator:
315
  imports.append(f"@import url('{google_fonts_url}');")
316
  seen_urls.add(google_fonts_url)
317
 
318
- return "\n".join(imports)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
319
 
320
- def _create_dynamic_theme_css(self, top_hits: list[dict[str, any]]) -> str:
321
- """Generates a <style> block to apply fonts to different UI elements."""
322
  if not top_hits:
323
  return ""
324
 
325
- font_imports = self._create_font_imports_css(top_hits)
 
 
 
 
 
 
 
 
 
 
 
326
 
327
  css_rules = []
328
 
329
- # Apply different fonts to different elements
330
  if len(top_hits) >= 1:
331
  primary_font = self.font_data[top_hits[0]['corpus_id']]['name'].replace("'", "\\'")
332
  css_rules.append(f"h1, h2, h3, .gr-button-primary {{ font-family: '{primary_font}', sans-serif !important; }}")
@@ -346,7 +252,6 @@ class FontMoodGenerator:
346
 
347
  {css_rules_str}
348
 
349
- /* Custom animations for font changes */
350
  * {{
351
  transition: font-family 0.3s ease-in-out;
352
  }}
@@ -354,71 +259,12 @@ class FontMoodGenerator:
354
 
355
  return css
356
 
357
- def _create_css_code_output(self, top_hits: list[dict[str, any]]) -> str:
358
- """Creates exportable CSS code for users."""
359
- if not top_hits:
360
- return ""
361
-
362
- font_imports = self._create_font_imports_css(top_hits)
363
-
364
- css_code = f"""/* Generated Font Palette CSS */
365
- {font_imports}
366
-
367
- /* Font Variables */
368
- :root {{"""
369
-
370
- for i, hit in enumerate(top_hits):
371
- font_info = self.font_data[hit['corpus_id']]
372
- font_name = font_info['name']
373
- css_code += f"""
374
- --font-{i+1}: '{font_name}', {font_info['family']};"""
375
-
376
- css_code += """
377
- }
378
-
379
- /* Usage Examples */
380
- .heading { font-family: var(--font-1); }
381
- .body-text { font-family: var(--font-2); }
382
- .accent { font-family: var(--font-3); }"""
383
-
384
- return css_code
385
-
386
- def generate_palette(self, mood_text: str) -> tuple[str, list[dict[str, any]]]:
387
- """Generates font palette and returns both HTML and raw data."""
388
- if not mood_text or not mood_text.strip():
389
- return "<p>Please enter a mood or a description.</p>", []
390
-
391
- mood_embedding = self.embedding_model.encode(
392
- mood_text,
393
- prompt_name=self.config.PROMPT_NAME
394
- )
395
- top_hits = util.semantic_search(
396
- mood_embedding, self.font_embeddings, top_k=self.config.TOP_K
397
- )[0]
398
-
399
- palette_html = self._format_palette_as_html(top_hits)
400
- return palette_html, top_hits
401
-
402
- def apply_theme(self, top_hits: list[dict[str, any]]) -> str:
403
- """Applies the theme CSS."""
404
- return self._create_dynamic_theme_css(top_hits)
405
-
406
- def generate_css_code(self, top_hits: list[dict[str, any]]) -> str:
407
- """Generates exportable CSS code."""
408
- return self._create_css_code_output(top_hits)
409
-
410
 
411
  # --- GRADIO UI WITH WALKTHROUGH ---
412
-
413
  def create_ui(generator: FontMoodGenerator):
414
  """Creates the Gradio web interface with Walkthrough."""
415
 
416
- # Shared state to store generated fonts across steps
417
- font_state = gr.State([])
418
-
419
  with gr.Blocks(theme="ocean") as demo:
420
- # Dynamic CSS output
421
- dynamic_css_output = gr.HTML()
422
 
423
  gr.Markdown("""
424
  # 📝 Font Mood Generator
@@ -448,22 +294,27 @@ def create_ui(generator: FontMoodGenerator):
448
  "Modern tech startup with clean aesthetics",
449
  "Playful children's book with whimsical characters",
450
  "Horror movie poster with scary atmosphere",
451
- "Luxury fashion brand with sophisticated appeal",
452
- "Retro 1950s diner with nostalgic vibes",
453
  ],
454
  inputs=mood_input,
455
  )
456
 
457
  generate_btn = gr.Button("Generate Font Palette →", variant="primary", size="lg")
458
 
 
 
 
 
459
  def generate_and_move(mood_text):
460
  palette_html, top_hits = generator.generate_palette(mood_text)
461
- return palette_html, top_hits, gr.Walkthrough(selected=1)
 
 
462
 
463
  generate_btn.click(
464
  fn=generate_and_move,
465
  inputs=mood_input,
466
- outputs=[gr.State(), font_state, walkthrough]
467
  )
468
 
469
  # STEP 2: Review Generated Fonts
@@ -471,21 +322,10 @@ def create_ui(generator: FontMoodGenerator):
471
  gr.Markdown("""
472
  ### Step 2: Review your generated fonts
473
  Here are the fonts that best match your mood, ranked by similarity score.
474
- Each font is scored based on how well it captures your described aesthetic.
475
  """)
476
 
477
  palette_display = gr.HTML()
478
 
479
- def show_palette(palette_html):
480
- return palette_html
481
-
482
- # Update palette when entering this step
483
- demo.load(
484
- fn=show_palette,
485
- inputs=gr.State(),
486
- outputs=palette_display
487
- )
488
-
489
  with gr.Row():
490
  back_to_input_btn = gr.Button("← Back to Input", variant="secondary")
491
  apply_theme_btn = gr.Button("Apply Typography Theme →", variant="primary", size="lg")
@@ -495,29 +335,51 @@ def create_ui(generator: FontMoodGenerator):
495
  outputs=walkthrough
496
  )
497
 
498
- def apply_and_move(top_hits):
499
- theme_css = generator.apply_theme(top_hits)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
500
  return theme_css, gr.Walkthrough(selected=2)
501
 
502
  apply_theme_btn.click(
503
- fn=apply_and_move,
504
- inputs=font_state,
505
- outputs=[dynamic_css_output, walkthrough]
506
  )
507
 
508
  # STEP 3: Experience the Typography
509
  with gr.Step("✨ Experience Your Typography", id=2):
510
  gr.Markdown("""
511
  ### Step 3: See your fonts in action!
512
- Notice how the entire interface has transformed to reflect your chosen aesthetic.
513
- The fonts from your palette are now applied throughout the UI.
514
  """)
515
 
 
 
 
 
 
 
 
 
 
516
  gr.Markdown("""
517
  **🎉 Your typography theme is now active!**
518
 
519
- Look around the interface - the headings, buttons, and text inputs now use fonts from your generated palette.
520
- This gives you a real preview of how these fonts work together in a design context.
521
 
522
  **Font Roles:**
523
  - **Primary Font**: Used for headings and primary buttons
@@ -549,17 +411,20 @@ def create_ui(generator: FontMoodGenerator):
549
  css_code_output = gr.Code(
550
  language="css",
551
  label="Your Font Palette CSS",
552
- value="",
553
  lines=15
554
  )
555
 
556
- def generate_css_code(top_hits):
 
 
 
 
557
  return generator.generate_css_code(top_hits)
558
 
559
- # Generate CSS when entering this step
560
- demo.load(
561
- fn=generate_css_code,
562
- inputs=font_state,
563
  outputs=css_code_output
564
  )
565
 
@@ -569,19 +434,16 @@ def create_ui(generator: FontMoodGenerator):
569
  2. Include it in your website's stylesheet
570
  3. Apply the font variables to your HTML elements
571
  4. Enjoy your new typography!
572
-
573
- **💡 Pro Tip:** You can also use individual Google Fonts links if you prefer to load fonts separately.
574
  """)
575
 
576
- with gr.Row():
577
- start_over_btn = gr.Button("🔄 Start Over", variant="secondary", size="lg")
578
-
579
  def restart():
580
- return "", [], gr.Walkthrough(selected=0)
581
 
582
  start_over_btn.click(
583
  fn=restart,
584
- outputs=[dynamic_css_output, font_state, walkthrough]
585
  )
586
 
587
  # Static CSS for font cards
@@ -629,30 +491,9 @@ def create_ui(generator: FontMoodGenerator):
629
  .font-description {
630
  color: #5d6d7e; font-size: 0.9em; line-height: 1.4;
631
  }
632
-
633
- /* Walkthrough styling enhancements */
634
- .gr-walkthrough {
635
- border-radius: 12px;
636
- overflow: hidden;
637
- }
638
  </style>
639
  """)
640
 
641
- gr.Markdown("""
642
- ----
643
- ## About This App
644
-
645
- This **Font Mood Generator** uses the new `gr.Walkthrough` component to create a guided, step-by-step experience for generating typography palettes.
646
-
647
- The app is powered by [**EmbeddingGemma**](http://huggingface.co/google/embeddinggemma-300M), Google's text embedding model that understands the semantic meaning and emotional qualities of your descriptions to find matching fonts.
648
-
649
- **🆕 Walkthrough Features:**
650
- - **Guided Experience**: Step-by-step workflow for better user experience
651
- - **Progressive Disclosure**: Information and controls revealed when needed
652
- - **Visual Progress**: Clear indication of current step and progress
653
- - **Interactive Navigation**: Ability to go back and forth between steps
654
- """)
655
-
656
  return demo
657
 
658
 
 
5
  from sentence_transformers import SentenceTransformer, util
6
 
7
  # --- CONFIGURATION ---
 
 
8
  class Config:
9
  """Configuration settings for the application."""
10
  EMBEDDING_MODEL_ID = "google/embeddinggemma-300M"
 
12
  TOP_K = 5
13
  HF_TOKEN = os.getenv('HF_TOKEN')
14
 
 
15
  # --- FONT DATA ---
 
 
16
  FONT_DATA = [
17
  {
18
  "name": "Playfair Display",
 
74
  "google_fonts_url": "https://fonts.googleapis.com/css2?family=Pacifico&display=swap",
75
  "description": "Surfing, California, retro, casual script font with beach vibes and laid-back summer feeling"
76
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  ]
78
 
 
79
  # --- CORE LOGIC ---
 
 
80
  class FontMoodGenerator:
81
  """Handles model loading, embedding generation, and font palette creation."""
82
 
 
95
  login(token=self.config.HF_TOKEN)
96
  else:
97
  print("HF_TOKEN not found. Proceeding without login.")
 
98
 
99
  def _load_model(self) -> SentenceTransformer:
100
  """Loads the Sentence Transformer model."""
 
124
  if not top_hits:
125
  return "<p>Could not generate a font palette. Please try another mood.</p>"
126
 
 
127
  sample_texts = [
128
  "The Quick Brown Fox Jumps Over The Lazy Dog",
129
  "Sphinx of black quartz, judge my vow",
 
157
  """
158
  return f"<div class='font-palette-container'>{cards_html}</div>"
159
 
160
+ def generate_palette(self, mood_text: str) -> tuple[str, list[dict[str, any]]]:
161
+ """Generates font palette and returns both HTML and raw data."""
162
+ if not mood_text or not mood_text.strip():
163
+ return "<p>Please enter a mood or a description.</p>", []
164
+
165
+ mood_embedding = self.embedding_model.encode(
166
+ mood_text,
167
+ prompt_name=self.config.PROMPT_NAME
168
+ )
169
+ top_hits = util.semantic_search(
170
+ mood_embedding, self.font_embeddings, top_k=self.config.TOP_K
171
+ )[0]
172
 
173
+ palette_html = self._format_palette_as_html(top_hits)
174
+ return palette_html, top_hits
175
+
176
+ def generate_css_code(self, top_hits: list[dict[str, any]]) -> str:
177
+ """Generates exportable CSS code."""
178
+ if not top_hits:
179
+ return "/* No fonts generated yet */"
180
+
181
  imports = []
182
  seen_urls = set()
183
 
 
189
  imports.append(f"@import url('{google_fonts_url}');")
190
  seen_urls.add(google_fonts_url)
191
 
192
+ font_imports = "\n".join(imports)
193
+
194
+ css_code = f"""/* Generated Font Palette CSS */
195
+ {font_imports}
196
+
197
+ /* Font Variables */
198
+ :root {{"""
199
+
200
+ for i, hit in enumerate(top_hits):
201
+ font_info = self.font_data[hit['corpus_id']]
202
+ font_name = font_info['name']
203
+ css_code += f"""
204
+ --font-{i+1}: '{font_name}', {font_info['family']};"""
205
+
206
+ css_code += """
207
+ }
208
+
209
+ /* Usage Examples */
210
+ .heading { font-family: var(--font-1); }
211
+ .body-text { font-family: var(--font-2); }
212
+ .accent { font-family: var(--font-3); }"""
213
+
214
+ return css_code
215
 
216
+ def apply_theme_css(self, top_hits: list[dict[str, any]]) -> str:
217
+ """Generates CSS to apply fonts to the UI."""
218
  if not top_hits:
219
  return ""
220
 
221
+ imports = []
222
+ seen_urls = set()
223
+
224
+ for hit in top_hits:
225
+ font_info = self.font_data[hit['corpus_id']]
226
+ google_fonts_url = font_info['google_fonts_url']
227
+
228
+ if google_fonts_url not in seen_urls:
229
+ imports.append(f"@import url('{google_fonts_url}');")
230
+ seen_urls.add(google_fonts_url)
231
+
232
+ font_imports = "\n".join(imports)
233
 
234
  css_rules = []
235
 
 
236
  if len(top_hits) >= 1:
237
  primary_font = self.font_data[top_hits[0]['corpus_id']]['name'].replace("'", "\\'")
238
  css_rules.append(f"h1, h2, h3, .gr-button-primary {{ font-family: '{primary_font}', sans-serif !important; }}")
 
252
 
253
  {css_rules_str}
254
 
 
255
  * {{
256
  transition: font-family 0.3s ease-in-out;
257
  }}
 
259
 
260
  return css
261
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
262
 
263
  # --- GRADIO UI WITH WALKTHROUGH ---
 
264
  def create_ui(generator: FontMoodGenerator):
265
  """Creates the Gradio web interface with Walkthrough."""
266
 
 
 
 
267
  with gr.Blocks(theme="ocean") as demo:
 
 
268
 
269
  gr.Markdown("""
270
  # 📝 Font Mood Generator
 
294
  "Modern tech startup with clean aesthetics",
295
  "Playful children's book with whimsical characters",
296
  "Horror movie poster with scary atmosphere",
297
+ "Luxury fashion brand with sophisticated appeal"
 
298
  ],
299
  inputs=mood_input,
300
  )
301
 
302
  generate_btn = gr.Button("Generate Font Palette →", variant="primary", size="lg")
303
 
304
+ # Hidden outputs to store results
305
+ palette_html_hidden = gr.HTML(visible=False)
306
+ font_data_hidden = gr.JSON(visible=False)
307
+
308
  def generate_and_move(mood_text):
309
  palette_html, top_hits = generator.generate_palette(mood_text)
310
+ # Convert to serializable format
311
+ font_data_json = [{"corpus_id": hit["corpus_id"], "score": hit["score"]} for hit in top_hits]
312
+ return palette_html, font_data_json, gr.Walkthrough(selected=1)
313
 
314
  generate_btn.click(
315
  fn=generate_and_move,
316
  inputs=mood_input,
317
+ outputs=[palette_html_hidden, font_data_hidden, walkthrough]
318
  )
319
 
320
  # STEP 2: Review Generated Fonts
 
322
  gr.Markdown("""
323
  ### Step 2: Review your generated fonts
324
  Here are the fonts that best match your mood, ranked by similarity score.
 
325
  """)
326
 
327
  palette_display = gr.HTML()
328
 
 
 
 
 
 
 
 
 
 
 
329
  with gr.Row():
330
  back_to_input_btn = gr.Button("← Back to Input", variant="secondary")
331
  apply_theme_btn = gr.Button("Apply Typography Theme →", variant="primary", size="lg")
 
335
  outputs=walkthrough
336
  )
337
 
338
+ # Update display when entering this step
339
+ def show_generated_palette(palette_html):
340
+ return palette_html
341
+
342
+ palette_html_hidden.change(
343
+ fn=show_generated_palette,
344
+ inputs=palette_html_hidden,
345
+ outputs=palette_display
346
+ )
347
+
348
+ # Hidden CSS output for theming
349
+ theme_css_hidden = gr.HTML(visible=False)
350
+
351
+ def apply_theme_and_move(font_data_json):
352
+ # Convert back to the format expected by apply_theme_css
353
+ top_hits = [{"corpus_id": item["corpus_id"], "score": item["score"]} for item in font_data_json]
354
+ theme_css = generator.apply_theme_css(top_hits)
355
  return theme_css, gr.Walkthrough(selected=2)
356
 
357
  apply_theme_btn.click(
358
+ fn=apply_theme_and_move,
359
+ inputs=font_data_hidden,
360
+ outputs=[theme_css_hidden, walkthrough]
361
  )
362
 
363
  # STEP 3: Experience the Typography
364
  with gr.Step("✨ Experience Your Typography", id=2):
365
  gr.Markdown("""
366
  ### Step 3: See your fonts in action!
367
+ Notice how the entire interface has transformed to reflect your chosen aesthetic.
 
368
  """)
369
 
370
+ # Apply CSS when entering this step
371
+ theme_css_display = gr.HTML()
372
+
373
+ theme_css_hidden.change(
374
+ fn=lambda css: css,
375
+ inputs=theme_css_hidden,
376
+ outputs=theme_css_display
377
+ )
378
+
379
  gr.Markdown("""
380
  **🎉 Your typography theme is now active!**
381
 
382
+ Look around the interface - the headings, buttons, and text inputs now use fonts from your generated palette.
 
383
 
384
  **Font Roles:**
385
  - **Primary Font**: Used for headings and primary buttons
 
411
  css_code_output = gr.Code(
412
  language="css",
413
  label="Your Font Palette CSS",
414
+ value="/* Generate a palette first to see CSS code here */",
415
  lines=15
416
  )
417
 
418
+ # Update CSS code when font data changes
419
+ def update_css_code(font_data_json):
420
+ if not font_data_json:
421
+ return "/* Generate a palette first to see CSS code here */"
422
+ top_hits = [{"corpus_id": item["corpus_id"], "score": item["score"]} for item in font_data_json]
423
  return generator.generate_css_code(top_hits)
424
 
425
+ font_data_hidden.change(
426
+ fn=update_css_code,
427
+ inputs=font_data_hidden,
 
428
  outputs=css_code_output
429
  )
430
 
 
434
  2. Include it in your website's stylesheet
435
  3. Apply the font variables to your HTML elements
436
  4. Enjoy your new typography!
 
 
437
  """)
438
 
439
+ start_over_btn = gr.Button("🔄 Start Over", variant="secondary", size="lg")
440
+
 
441
  def restart():
442
+ return "", [], "", gr.Walkthrough(selected=0)
443
 
444
  start_over_btn.click(
445
  fn=restart,
446
+ outputs=[palette_html_hidden, font_data_hidden, theme_css_hidden, walkthrough]
447
  )
448
 
449
  # Static CSS for font cards
 
491
  .font-description {
492
  color: #5d6d7e; font-size: 0.9em; line-height: 1.4;
493
  }
 
 
 
 
 
 
494
  </style>
495
  """)
496
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
497
  return demo
498
 
499