ysharma HF Staff commited on
Commit
7c980a0
·
verified ·
1 Parent(s): dfd203c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +151 -167
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",
@@ -213,10 +208,7 @@ FONT_DATA = [
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 +227,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."""
@@ -260,12 +251,32 @@ class FontMoodGenerator:
260
  print("Embeddings computed successfully.")
261
  return embeddings
262
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
  def _format_palette_as_html(self, top_hits: list[dict[str, any]]) -> str:
264
- """Formats the top font hits into a displayable HTML string."""
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",
@@ -297,39 +308,72 @@ class FontMoodGenerator:
297
  </div>
298
  </div>
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
 
310
- for hit in top_hits:
311
  font_info = self.font_data[hit['corpus_id']]
312
- google_fonts_url = font_info['google_fonts_url']
313
-
314
- if google_fonts_url not in seen_urls:
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; }}")
333
 
334
  if len(top_hits) >= 2:
335
  secondary_font = self.font_data[top_hits[1]['corpus_id']]['name'].replace("'", "\\'")
@@ -341,12 +385,16 @@ class FontMoodGenerator:
341
 
342
  css_rules_str = "\n ".join(css_rules)
343
 
344
- css = f"""<style>
345
  {font_imports}
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 +402,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
@@ -436,7 +425,7 @@ def create_ui(generator: FontMoodGenerator):
436
  """)
437
 
438
  mood_input = gr.Textbox(
439
- value="Elegant wedding invitation with vintage charm",
440
  label="Enter Your Mood or Scene",
441
  info="Examples: 'Modern tech startup', 'Playful children's book', 'Gothic horror movie'",
442
  lines=3
@@ -448,22 +437,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 +465,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 +478,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 +554,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,90 +577,66 @@ 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
588
  gr.HTML("""
589
  <style>
590
  .font-palette-container {
591
- display: flex; flex-direction: column; gap: 15px;
592
  align-items: center; width: 100%;
593
  }
594
  .font-card {
595
- border: 2px solid #e0e0e0; border-radius: 12px;
596
- padding: 20px; width: 90%; max-width: 600px;
597
  background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
598
- box-shadow: 0 4px 12px rgba(0,0,0,0.1);
599
  transition: all 0.3s ease;
600
  }
601
  .font-card:hover {
602
- transform: translateY(-2px);
603
- box-shadow: 0 8px 20px rgba(0,0,0,0.15);
604
  }
605
  .font-header {
606
  display: flex; justify-content: space-between;
607
- align-items: center; margin-bottom: 15px;
608
  }
609
  .font-header h3 {
610
- margin: 0; color: #2c3e50; font-size: 1.2em;
611
  }
612
  .font-score {
613
- background: #3498db; color: white; padding: 4px 8px;
614
- border-radius: 12px; font-size: 0.8em; font-weight: bold;
615
  }
616
  .font-sample {
617
- font-size: 24px; line-height: 1.4; margin: 15px 0;
618
- padding: 15px; background: #f8f9fa; border-radius: 8px;
619
- border-left: 4px solid #3498db; color: #2c3e50;
620
- min-height: 60px; display: flex; align-items: center;
621
  }
622
  .font-details {
623
- display: flex; flex-direction: column; gap: 8px;
624
  }
625
  .font-family {
626
- font-weight: bold; color: #7f8c8d; font-size: 0.9em;
627
  text-transform: uppercase; letter-spacing: 1px;
628
  }
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",
 
208
  }
209
  ]
210
 
 
211
  # --- CORE LOGIC ---
 
 
212
  class FontMoodGenerator:
213
  """Handles model loading, embedding generation, and font palette creation."""
214
 
 
227
  login(token=self.config.HF_TOKEN)
228
  else:
229
  print("HF_TOKEN not found. Proceeding without login.")
 
230
 
231
  def _load_model(self) -> SentenceTransformer:
232
  """Loads the Sentence Transformer model."""
 
251
  print("Embeddings computed successfully.")
252
  return embeddings
253
 
254
+ def _create_persistent_font_imports(self, top_hits: list[dict[str, any]]) -> str:
255
+ """Creates persistent font imports that work across all steps."""
256
+ if not top_hits:
257
+ return ""
258
+
259
+ imports = []
260
+ seen_urls = set()
261
+
262
+ for hit in top_hits:
263
+ font_info = self.font_data[hit['corpus_id']]
264
+ google_fonts_url = font_info['google_fonts_url']
265
+
266
+ if google_fonts_url not in seen_urls:
267
+ imports.append(f"@import url('{google_fonts_url}');")
268
+ seen_urls.add(google_fonts_url)
269
+
270
+ return "\n".join(imports)
271
+
272
  def _format_palette_as_html(self, top_hits: list[dict[str, any]]) -> str:
273
+ """Formats the top font hits into a displayable HTML string with embedded font imports."""
274
  if not top_hits:
275
  return "<p>Could not generate a font palette. Please try another mood.</p>"
276
 
277
+ # Get font imports for the selected fonts
278
+ font_imports = self._create_persistent_font_imports(top_hits)
279
+
280
  sample_texts = [
281
  "The Quick Brown Fox Jumps Over The Lazy Dog",
282
  "Sphinx of black quartz, judge my vow",
 
308
  </div>
309
  </div>
310
  """
311
+
312
+ # Include font imports directly in the HTML with unique ID to prevent conflicts
313
+ return f"""
314
+ <style id="palette-fonts">
315
+ {font_imports}
316
+ </style>
317
+ <div class='font-palette-container'>{cards_html}</div>
318
+ """
319
+
320
+ def generate_palette(self, mood_text: str) -> tuple[str, list[dict[str, any]]]:
321
+ """Generates font palette and returns both HTML and raw data."""
322
+ if not mood_text or not mood_text.strip():
323
+ return "<p>Please enter a mood or a description.</p>", []
324
 
325
+ mood_embedding = self.embedding_model.encode(
326
+ mood_text,
327
+ prompt_name=self.config.PROMPT_NAME
328
+ )
329
+ top_hits = util.semantic_search(
330
+ mood_embedding, self.font_embeddings, top_k=self.config.TOP_K
331
+ )[0]
332
+
333
+ palette_html = self._format_palette_as_html(top_hits)
334
+ return palette_html, top_hits
335
+
336
+ def generate_css_code(self, top_hits: list[dict[str, any]]) -> str:
337
+ """Generates exportable CSS code."""
338
  if not top_hits:
339
+ return "/* No fonts generated yet */"
340
+
341
+ font_imports = self._create_persistent_font_imports(top_hits)
342
+
343
+ css_code = f"""/* Generated Font Palette CSS */
344
+ {font_imports}
345
 
346
+ /* Font Variables */
347
+ :root {{"""
348
 
349
+ for i, hit in enumerate(top_hits):
350
  font_info = self.font_data[hit['corpus_id']]
351
+ font_name = font_info['name']
352
+ css_code += f"""
353
+ --font-{i+1}: '{font_name}', {font_info['family']};"""
354
+
355
+ css_code += """
356
+ }
357
 
358
+ /* Usage Examples */
359
+ .heading { font-family: var(--font-1); }
360
+ .body-text { font-family: var(--font-2); }
361
+ .accent { font-family: var(--font-3); }"""
362
+
363
+ return css_code
364
 
365
+ def apply_theme_css(self, top_hits: list[dict[str, any]]) -> str:
366
+ """Generates CSS to apply fonts to the UI while preserving palette fonts."""
367
  if not top_hits:
368
  return ""
369
 
370
+ font_imports = self._create_persistent_font_imports(top_hits)
371
 
372
  css_rules = []
373
 
 
374
  if len(top_hits) >= 1:
375
  primary_font = self.font_data[top_hits[0]['corpus_id']]['name'].replace("'", "\\'")
376
+ css_rules.append(f"h1, h2, h3:not(.font-card h3), .gr-button-primary {{ font-family: '{primary_font}', sans-serif !important; }}")
377
 
378
  if len(top_hits) >= 2:
379
  secondary_font = self.font_data[top_hits[1]['corpus_id']]['name'].replace("'", "\\'")
 
385
 
386
  css_rules_str = "\n ".join(css_rules)
387
 
388
+ css = f"""<style id="theme-fonts">
389
  {font_imports}
390
 
391
  {css_rules_str}
392
 
393
+ /* Preserve palette fonts */
394
+ .font-sample {{
395
+ font-family: inherit !important;
396
+ }}
397
+
398
  * {{
399
  transition: font-family 0.3s ease-in-out;
400
  }}
 
402
 
403
  return css
404
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
405
 
406
  # --- GRADIO UI WITH WALKTHROUGH ---
 
407
  def create_ui(generator: FontMoodGenerator):
408
  """Creates the Gradio web interface with Walkthrough."""
409
 
 
 
 
410
  with gr.Blocks(theme="ocean") as demo:
 
 
411
 
412
  gr.Markdown("""
413
  # 📝 Font Mood Generator
 
425
  """)
426
 
427
  mood_input = gr.Textbox(
428
+ value="Horror movie poster with scary atmosphere",
429
  label="Enter Your Mood or Scene",
430
  info="Examples: 'Modern tech startup', 'Playful children's book', 'Gothic horror movie'",
431
  lines=3
 
437
  "Modern tech startup with clean aesthetics",
438
  "Playful children's book with whimsical characters",
439
  "Horror movie poster with scary atmosphere",
440
+ "Luxury fashion brand with sophisticated appeal"
 
441
  ],
442
  inputs=mood_input,
443
  )
444
 
445
  generate_btn = gr.Button("Generate Font Palette →", variant="primary", size="lg")
446
 
447
+ # Hidden outputs to store results
448
+ palette_html_hidden = gr.HTML(visible=False)
449
+ font_data_hidden = gr.JSON(visible=False)
450
+
451
  def generate_and_move(mood_text):
452
  palette_html, top_hits = generator.generate_palette(mood_text)
453
+ # Convert to serializable format
454
+ font_data_json = [{"corpus_id": hit["corpus_id"], "score": hit["score"]} for hit in top_hits]
455
+ return palette_html, font_data_json, gr.Walkthrough(selected=1)
456
 
457
  generate_btn.click(
458
  fn=generate_and_move,
459
  inputs=mood_input,
460
+ outputs=[palette_html_hidden, font_data_hidden, walkthrough]
461
  )
462
 
463
  # STEP 2: Review Generated Fonts
 
465
  gr.Markdown("""
466
  ### Step 2: Review your generated fonts
467
  Here are the fonts that best match your mood, ranked by similarity score.
 
468
  """)
469
 
470
  palette_display = gr.HTML()
471
 
 
 
 
 
 
 
 
 
 
 
472
  with gr.Row():
473
  back_to_input_btn = gr.Button("← Back to Input", variant="secondary")
474
  apply_theme_btn = gr.Button("Apply Typography Theme →", variant="primary", size="lg")
 
478
  outputs=walkthrough
479
  )
480
 
481
+ # Update display when entering this step
482
+ def show_generated_palette(palette_html):
483
+ return palette_html
484
+
485
+ palette_html_hidden.change(
486
+ fn=show_generated_palette,
487
+ inputs=palette_html_hidden,
488
+ outputs=palette_display
489
+ )
490
+
491
+ # Hidden CSS output for theming
492
+ theme_css_hidden = gr.HTML(visible=False)
493
+
494
+ def apply_theme_and_move(font_data_json):
495
+ # Convert back to the format expected by apply_theme_css
496
+ top_hits = [{"corpus_id": item["corpus_id"], "score": item["score"]} for item in font_data_json]
497
+ theme_css = generator.apply_theme_css(top_hits)
498
  return theme_css, gr.Walkthrough(selected=2)
499
 
500
  apply_theme_btn.click(
501
+ fn=apply_theme_and_move,
502
+ inputs=font_data_hidden,
503
+ outputs=[theme_css_hidden, walkthrough]
504
  )
505
 
506
  # STEP 3: Experience the Typography
507
  with gr.Step("✨ Experience Your Typography", id=2):
508
  gr.Markdown("""
509
  ### Step 3: See your fonts in action!
510
+ Notice how the entire interface has transformed to reflect your chosen aesthetic.
 
511
  """)
512
 
513
+ # Apply CSS when entering this step
514
+ theme_css_display = gr.HTML()
515
+
516
+ theme_css_hidden.change(
517
+ fn=lambda css: css,
518
+ inputs=theme_css_hidden,
519
+ outputs=theme_css_display
520
+ )
521
+
522
  gr.Markdown("""
523
  **🎉 Your typography theme is now active!**
524
 
525
+ Look around the interface - the headings, buttons, and text inputs now use fonts from your generated palette.
 
526
 
527
  **Font Roles:**
528
  - **Primary Font**: Used for headings and primary buttons
 
554
  css_code_output = gr.Code(
555
  language="css",
556
  label="Your Font Palette CSS",
557
+ value="/* Generate a palette first to see CSS code here */",
558
  lines=15
559
  )
560
 
561
+ # Update CSS code when font data changes
562
+ def update_css_code(font_data_json):
563
+ if not font_data_json:
564
+ return "/* Generate a palette first to see CSS code here */"
565
+ top_hits = [{"corpus_id": item["corpus_id"], "score": item["score"]} for item in font_data_json]
566
  return generator.generate_css_code(top_hits)
567
 
568
+ font_data_hidden.change(
569
+ fn=update_css_code,
570
+ inputs=font_data_hidden,
 
571
  outputs=css_code_output
572
  )
573
 
 
577
  2. Include it in your website's stylesheet
578
  3. Apply the font variables to your HTML elements
579
  4. Enjoy your new typography!
 
 
580
  """)
581
 
582
+ start_over_btn = gr.Button("🔄 Start Over", variant="secondary", size="lg")
583
+
 
584
  def restart():
585
+ return "", [], "", "", gr.Walkthrough(selected=0)
586
 
587
  start_over_btn.click(
588
  fn=restart,
589
+ outputs=[palette_html_hidden, font_data_hidden, theme_css_hidden, theme_css_display, walkthrough]
590
  )
591
 
592
+ # Static CSS for font cards (always loaded)
593
  gr.HTML("""
594
  <style>
595
  .font-palette-container {
596
+ display: flex; flex-direction: column; gap: 8px;
597
  align-items: center; width: 100%;
598
  }
599
  .font-card {
600
+ border: 2px solid #e0e0e0; border-radius: 8px;
601
+ padding: 10px; width: 90%; max-width: 600px;
602
  background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
603
+ box-shadow: 0 2px 6px rgba(0,0,0,0.1);
604
  transition: all 0.3s ease;
605
  }
606
  .font-card:hover {
607
+ transform: translateY(-1px);
608
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
609
  }
610
  .font-header {
611
  display: flex; justify-content: space-between;
612
+ align-items: center; margin-bottom: 8px;
613
  }
614
  .font-header h3 {
615
+ margin: 0; color: #2c3e50; font-size: 1.1em;
616
  }
617
  .font-score {
618
+ background: #3498db; color: white; padding: 3px 6px;
619
+ border-radius: 8px; font-size: 0.75em; font-weight: bold;
620
  }
621
  .font-sample {
622
+ font-size: 18px; line-height: 1.2; margin: 8px 0;
623
+ padding: 8px; background: #f8f9fa; border-radius: 6px;
624
+ border-left: 3px solid #3498db; color: #2c3e50;
625
+ min-height: 30px; display: flex; align-items: center;
626
  }
627
  .font-details {
628
+ display: flex; flex-direction: column; gap: 4px;
629
  }
630
  .font-family {
631
+ font-weight: bold; color: #7f8c8d; font-size: 0.8em;
632
  text-transform: uppercase; letter-spacing: 1px;
633
  }
634
  .font-description {
635
+ color: #5d6d7e; font-size: 0.85em; line-height: 1.3;
 
 
 
 
 
 
636
  }
637
  </style>
638
  """)
639
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
640
  return demo
641
 
642