mroccuper commited on
Commit
2911850
Β·
verified Β·
1 Parent(s): 11e611b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +289 -489
app.py CHANGED
@@ -10,6 +10,7 @@ import random
10
  css = """
11
  body {
12
  font-family: 'Poppins', -apple-system, BlinkMacSystemFont, sans-serif;
 
13
  }
14
  .container {
15
  max-width: 900px;
@@ -32,9 +33,6 @@ body {
32
  margin-top: 15px;
33
  font-family: 'Courier New', monospace;
34
  white-space: pre-wrap;
35
- #border-radius: 8px;
36
- #background-color: #f9f9f9;
37
- #border: 1px solid #eee;
38
  }
39
  footer {
40
  text-align: center;
@@ -47,8 +45,6 @@ footer {
47
  .api-key-container {
48
  padding: 20px;
49
  margin-bottom: 25px;
50
- #background-color: #f5f5f5;
51
- #border-radius: 8px;
52
  }
53
  .prompt-container {
54
  margin-bottom: 20px;
@@ -68,9 +64,13 @@ footer {
68
  }
69
  .examples-container {
70
  margin-top: 15px;
71
- padding: 15px;
72
- #border-radius: 8px;
73
- #background-color: #f0f0f0;
 
 
 
 
74
  }
75
  .logo {
76
  text-align: center;
@@ -80,561 +80,361 @@ footer {
80
  max-width: 80px;
81
  height: auto;
82
  }
 
 
 
83
  .style-options {
84
  margin-top: 15px;
85
- padding: 15px;
86
- #border-radius: 8px;
87
- #background-color: #f5f5f5;
88
  }
89
  .market-box {
90
  margin-top: 15px;
91
  padding: 15px;
92
- #border: 1px solid #ddd;
93
- #border-radius: 8px;
94
- #background-color: #f9f9f9;
95
  }
96
  .keyword-list {
97
  margin-top: 5px;
98
  font-family: 'Courier New', monospace;
99
  }
100
- .style-cards {
101
- display: grid;
102
- grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
103
- gap: 15px;
104
- margin-top: 20px;
105
- }
106
- .style-card {
107
- #border: 1px solid #ddd;
108
- #border-radius: 8px;
109
- padding: 15px;
110
- background-color: white;
111
- transition: transform 0.2s, box-shadow 0.2s;
112
- }
113
- .style-card:hover {
114
- transform: translateY(-3px);
115
- box-shadow: 0 5px 15px rgba(0,0,0,0.1);
116
- }
117
- .style-card.selected {
118
- border: 2px solid #e94560;
119
- background-color: #fff5f7;
120
- }
121
- .style-title {
122
- font-weight: bold;
123
- margin-bottom: 5px;
124
- color: #333;
125
- }
126
- .style-desc {
127
- font-size: 0.9em;
128
- color: #666;
129
- }
130
- .pod-info {
131
- display: flex;
132
- margin-top: 10px;
133
- font-size: 0.8em;
134
- }
135
- .pod-colors, .pod-products {
136
- flex: 1;
137
- color: #000;
138
- }
139
- .pod-colors span, .pod-products span {
140
- background: #f0f0f0;
141
- padding: 2px 6px;
142
- border-radius: 4px;
143
- margin-right: 4px;
144
- white-space: nowrap;
145
- display: inline-block;
146
- margin-bottom: 4px;
147
- color: #000;
148
- }
149
- .analytics-section {
150
- margin-top: 20px;
151
- padding: 15px;
152
- #border-radius: 8px;
153
- background-color: #f0f8ff;
154
- #border: 1px solid #d0e0ff;
155
- }
156
  """
157
 
158
- # Define POD styles
159
- POD_STYLES = {
160
- "Auto-Detect": {
161
- "guidance": "AI will determine the best style based on quote analysis",
162
- "colors": ["Adaptive Colors"],
163
- "products": ["Various Products"]
164
- },
165
- "Streetwear_Urban": {
166
- "guidance": "Focus on bold, urban streetwear typography with graffiti influences, distressed elements, and street culture aesthetics. Perfect for hoodies and t-shirts targeting youth culture.",
167
- "colors": ["Black", "White", "Bold Accents"],
168
- "products": ["Hoodies", "T-shirts", "Snapbacks"]
169
- },
170
- "Vintage_Retro": {
171
- "guidance": "Focus on nostalgic vintage typography with distressed textures, retro fonts, and classic elements from the 50s-90s era. Include aged effects and throwback aesthetics.",
172
- "colors": ["Faded", "Weathered", "Retro Palettes"],
173
- "products": ["T-shirts", "Hoodies", "Tanks"]
174
- },
175
- "Minimalist_Clean": {
176
- "guidance": "Focus on clean, minimal typography with ample white space, elegant simple fonts, and refined layouts. Prioritize readability and sophistication with subtle design elements.",
177
- "colors": ["Black", "White", "Grey", "Neutrals"],
178
- "products": ["Premium T-shirts", "Hoodies", "Long Sleeves"]
179
- },
180
- "Motivational_Fitness": {
181
- "guidance": "Focus on dynamic, energetic typography for fitness and motivational quotes. Use strong, powerful fonts with dynamic layouts that convey strength, movement and determination.",
182
- "colors": ["Bold", "High-contrast", "Energetic"],
183
- "products": ["Workout Tees", "Gym Hoodies", "Tanks"]
184
- },
185
- "Family_Sentimental": {
186
- "guidance": "Focus on heartfelt, emotional typography designs for family quotes. Use warm, inviting fonts with decorative elements like hearts, flourishes, and family-oriented symbols.",
187
- "colors": ["Warm", "Comforting", "Heartfelt"],
188
- "products": ["Family T-shirts", "Gift Hoodies"]
189
- },
190
- "Funny_Humor": {
191
- "guidance": "Focus on playful, humorous typography with comic-style fonts, fun layouts, and witty visual elements that enhance the humorous quote. Include playful decorations.",
192
- "colors": ["Bright", "Playful", "Attention-grabbing"],
193
- "products": ["Novelty Tees", "Humorous Hoodies"]
194
- },
195
- }
196
-
197
- # Function to generate prompts using Gemini API
198
- def generate_typography_prompts(api_key, quote, num_prompts=10, style_preference=None):
199
- # Configure the Gemini API
200
  genai.configure(api_key=api_key)
201
 
202
- # Create a model
203
  model = genai.GenerativeModel(
204
  model_name="gemini-1.5-pro",
205
  generation_config={
206
- "temperature": 0.9,
207
  "top_p": 0.95,
208
  "top_k": 40,
209
- "max_output_tokens": 4000,
210
  }
211
  )
212
 
213
- # Style guidance based on preference
214
- style_guidance = ""
215
- if style_preference and style_preference in POD_STYLES:
216
- style_guidance = POD_STYLES[style_preference]["guidance"]
217
-
218
- # Craft a detailed prompt for Gemini
219
- system_prompt = f"""You are a creative typography and print-on-demand design expert.
220
-
221
- Create {num_prompts} unique, creative typography design prompts for the following quote:
222
- "{quote}"
223
 
224
- Each prompt should be specifically optimized for print-on-demand products like t-shirts and hoodies.
225
- Include these elements in each prompt:
226
- 1. A creative, visually striking typography design description
227
- 2. Specific design details (fonts, layout, decorative elements, textures)
228
- 3. POD-specific considerations (placement on garment, sizing, print technique hints)
229
- 4. "Target Audience:" section naming the specific demographic this design would appeal to
230
- 5. "Best Products:" section suggesting 2-3 specific POD products this design works best on
231
 
232
- {style_guidance}
 
 
233
 
234
- Use the following format for each prompt:
235
- [Detailed typography design description with specific visual details]
236
- Target Audience: [Specific demographic description]
237
- Best Products: [2-3 specific POD products this design works best on]
238
 
239
- Make each prompt distinct and highly creative. Focus on designs that would translate well to print-on-demand garments.
240
- The style should match both the mood of the quote and the intended POD application.
 
 
 
241
 
242
- Examples based on the quote "rise and grind":
243
- 1. Bold, industrial-style typography with "RISE" stacked above "GRIND" in distressed metal-look lettering. Add gear and mechanical elements around the edges with coffee splatter accents. Place centered on chest for maximum impact, with slightly weathered print effect.
244
- Target Audience: Entrepreneurial hustlers, coffee enthusiasts, gym-goers with morning routines
245
- Best Products: Black premium t-shirt, dark heather hoodie, athletic tank top
 
246
 
247
- 2. Minimalist sunrise gradient typography where "rise" appears in elegant thin script rising upward, while "and grind" follows in small, clean sans-serif below. Create subtle coffee bean silhouettes integrated into the letter spacing. Positioned slightly higher on chest for a modern look.
248
- Target Audience: Professional go-getters, minimalist style enthusiasts, coffee shop regulars
249
- Best Products: Premium fitted t-shirt, lightweight hoodie, long sleeve tee
250
- """
 
251
 
252
- try:
253
- # Generate content
254
- response = model.generate_content([system_prompt])
255
-
256
- # Extract the text from the response
257
- result = response.text
258
-
259
- # Post-process to ensure format consistency
260
- prompts = []
261
-
262
- # Split into individual prompts and clean up
263
- raw_prompts = re.split(r'\d+\.\s+', result)[1:] # Skip first empty element
264
-
265
- if not raw_prompts: # If splitting didn't work, try another approach
266
- raw_prompts = result.split("\n\n")
267
-
268
- for prompt in raw_prompts:
269
- # Clean up the prompt and ensure it has proper structure
270
- prompt = prompt.strip()
271
- if not prompt:
272
- continue
273
-
274
- # Ensure there's a Target Audience section
275
- if "Target Audience:" not in prompt:
276
- parts = prompt.split('\n')
277
- prompt = parts[0] + "\nTarget Audience: Print-on-demand customers, typography enthusiasts."
278
-
279
- # Ensure there's a Best Products section
280
- if "Best Products:" not in prompt:
281
- prompt += "\nBest Products: T-shirt, Hoodie, Long Sleeve Tee"
282
-
283
- prompts.append(prompt)
284
-
285
- # If we still don't have enough prompts, try another parsing method
286
- if len(prompts) < num_prompts/2:
287
- lines = result.split('\n')
288
- current_prompt = ""
289
-
290
- for line in lines:
291
- if re.match(r'^\d+\.', line): # New prompt begins
292
- if current_prompt:
293
- prompts.append(current_prompt.strip())
294
- current_prompt = line[line.find('.')+1:].strip()
295
- else:
296
- current_prompt = line[line.find('.')+1:].strip()
297
- else:
298
- current_prompt += "\n" + line
299
-
300
- if current_prompt: # Add the last prompt
301
- prompts.append(current_prompt.strip())
302
-
303
- # Ensure we have the right number of prompts
304
- prompts = prompts[:num_prompts]
305
-
306
- return prompts, None
307
-
308
- except Exception as e:
309
- return None, f"Error: {str(e)}"
310
 
311
- # Generate market keywords for Amazon products
312
- def generate_market_keywords(api_key, quote):
313
- # Configure the Gemini API
314
- genai.configure(api_key=api_key)
315
-
316
- # Create a model
317
- model = genai.GenerativeModel(
318
- model_name="gemini-1.5-pro",
319
- generation_config={
320
- "temperature": 0.7,
321
- "top_p": 0.95,
322
- "max_output_tokens": 1000,
323
- }
324
- )
325
-
326
- # Craft a detailed prompt for Gemini
327
- system_prompt = f"""Based on the following quote: "{quote}"
328
 
329
- 1. Identify up to 3 relevant holidays, events, or niches where this quote would be most marketable as a design on print-on-demand products (t-shirts, hoodies).
 
 
 
 
 
 
 
 
 
 
 
 
 
330
 
331
- 2. Generate 15-20 Amazon/Etsy search keywords that potential customers might use to find POD products featuring this quote.
 
 
 
 
 
 
 
 
 
 
332
 
333
- 3. Identify 3-5 specific customer personas who would be most likely to purchase this design.
334
 
335
- Format your response as follows:
 
 
 
 
 
 
 
 
 
 
 
336
 
337
- HOLIDAYS/EVENTS/NICHES:
338
- - [Holiday/Event/Niche 1]
339
- - [Holiday/Event/Niche 2]
340
- - [Holiday/Event/Niche 3]
341
 
342
- SEARCH KEYWORDS:
343
- [Keyword 1], [Keyword 2], [Keyword 3], etc.
 
 
344
 
345
- CUSTOMER PERSONAS:
346
- 1. [Name] - [Age range] - [Brief description including occupation and interests]
347
- 2. [Name] - [Age range] - [Brief description including occupation and interests]
348
- 3. [Name] - [Age range] - [Brief description including occupation and interests]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
 
350
- Make sure the keywords are relevant to the quote and would be used by actual shoppers looking for POD products with such messages. Focus on gift-giving occasions, product types, recipient types, and sentiment.
351
- """
352
 
353
- try:
354
- # Generate content
355
- response = model.generate_content([system_prompt])
356
-
357
- # Extract the text from the response
358
- result = response.text
359
-
360
- return result, None
361
-
362
- except Exception as e:
363
- return None, f"Error: {str(e)}"
364
-
365
- # Function to analyze quote and suggest the best style automatically
366
- def analyze_quote_for_style(api_key, quote):
367
- if not api_key or not quote:
368
- return "Auto-Detect" # Default
369
 
370
- # Configure the Gemini API
371
  genai.configure(api_key=api_key)
372
-
373
- # Create a model
374
  model = genai.GenerativeModel(
375
  model_name="gemini-1.5-pro",
376
- generation_config={
377
- "temperature": 0.3, # Lower temperature for more deterministic results
378
- "top_p": 0.95,
379
- "max_output_tokens": 500,
380
- }
381
  )
382
-
383
- # Get the style names without Auto-Detect
384
- style_options = list(POD_STYLES.keys())
385
- if "Auto-Detect" in style_options:
386
- style_options.remove("Auto-Detect")
387
-
388
- # Craft a detailed prompt for Gemini
389
- system_prompt = f"""Analyze the following quote for a t-shirt/hoodie design:
390
- "{quote}"
391
-
392
- Based on the quote's theme, tone, and content, select the MOST appropriate style category from this list:
393
- {', '.join(style_options)}
394
-
395
- Return only the exact name of the most appropriate style from the list, nothing else.
396
- """
397
-
398
  try:
399
- # Generate content
400
  response = model.generate_content([system_prompt])
401
-
402
- # Extract the text from the response
403
- result = response.text.strip()
404
-
405
- # Ensure the result matches one of our styles
406
- for style in style_options:
407
- if style.lower() in result.lower():
408
- return style
409
-
410
- # Default to the first style if no match
411
- return style_options[0]
412
-
413
  except Exception as e:
414
- return "Streetwear_Urban" # Default to first style if error
415
 
416
- # Function to handle form submission
417
- def process_quote(api_key, quote, num_prompts, style_preference, progress=gr.Progress()):
418
- if not api_key:
419
- return "Please enter your Gemini API key.", "", ""
420
 
421
- if not quote:
422
- return "Please enter a quote to generate typography prompts.", "", ""
423
-
424
- # Show progress and add small delays for better user experience
 
 
425
  progress(0, desc="Initializing...")
426
- time.sleep(0.3)
427
 
428
- progress(0.2, desc="Connecting to Gemini API...")
429
- time.sleep(0.3)
430
 
431
- # Auto-detect style if needed
432
- detected_style = style_preference
433
- if style_preference == "Auto-Detect":
434
- progress(0.3, desc="Analyzing quote for best style...")
435
- detected_style = analyze_quote_for_style(api_key, quote)
436
- auto_detect_info = f"Based on quote analysis, the AI selected the '{detected_style}' style."
437
- else:
438
- auto_detect_info = ""
439
-
440
- progress(0.5, desc="Generating creative prompts...")
441
- prompts, error = generate_typography_prompts(api_key, quote, num_prompts, detected_style)
442
 
443
  if error:
444
- return error, "", ""
445
-
446
- progress(0.7, desc="Identifying market opportunities...")
447
- market_info, market_error = generate_market_keywords(api_key, quote)
448
-
449
- if market_error:
450
- market_info = "Could not generate market information at this time."
451
 
452
- progress(0.9, desc="Formatting results...")
453
- time.sleep(0.3)
 
 
 
454
 
455
- # Format the prompts into a nice output
456
- result_text = f"Typography Prompts for: \"{quote}\"\n"
457
- if auto_detect_info:
458
- result_text += f"\n{auto_detect_info}\n"
459
- result_text += f"\nStyle: {detected_style}\n\n"
460
 
461
- for i, prompt in enumerate(prompts, 1):
462
- result_text += f"-- Prompt {i} --\n{prompt}\n\n"
 
 
 
463
 
464
  progress(1.0, desc="Done!")
465
- return result_text, market_info, detected_style
466
 
467
- # Example quotes for examples
468
  example_quotes = [
469
- "hustle mode: activated",
470
- "mama bear",
471
- "weekend vibes",
472
- "best dad ever",
473
- "sarcastic comment loading",
474
- "coffee first, then adulting"
475
  ]
476
 
477
- # Create style cards for selection
478
- def create_style_cards():
479
- style_cards_html = ""
480
- for style_id, style_data in POD_STYLES.items():
481
- # Create HTML for each style card
482
- colors_html = "".join([f'<span>{color}</span>' for color in style_data["colors"]])
483
- products_html = "".join([f'<span>{product}</span>' for product in style_data["products"]])
484
-
485
- style_cards_html += f"""
486
- <div class="style-card" id="style-{style_id}" onclick="selectStyle('{style_id}')">
487
- <div class="style-title">{style_id.replace('_', ' ')}</div>
488
- <div class="style-desc">{style_data['guidance'][:100]}...</div>
489
- <div class="pod-info">
490
- <div class="pod-colors">
491
- {colors_html}
492
- </div>
493
- <div class="pod-products">
494
- {products_html}
495
- </div>
496
- </div>
497
- </div>
498
- """
499
-
500
- # JavaScript for handling style selection
501
- js_code = """
502
- <script>
503
- function selectStyle(styleId) {
504
- // Remove selected class from all cards
505
- document.querySelectorAll('.style-card').forEach(card => {
506
- card.classList.remove('selected');
507
- });
508
-
509
- // Add selected class to clicked card
510
- document.getElementById('style-' + styleId).classList.add('selected');
511
-
512
- // Update the hidden style input
513
- const styleInput = document.getElementById('style-input');
514
- styleInput.value = styleId;
515
-
516
- // Trigger change event (for Gradio)
517
- const event = new Event('change');
518
- styleInput.dispatchEvent(event);
519
- }
520
-
521
- // Initialize with Auto-Detect selected
522
- document.addEventListener('DOMContentLoaded', () => {
523
- setTimeout(() => {
524
- selectStyle('Auto-Detect');
525
- }, 500);
526
- });
527
- </script>
528
- """
529
-
530
- return f"""
531
- <div class="style-cards">
532
- {style_cards_html}
533
- </div>
534
- {js_code}
535
- """
536
-
537
- # Create the Gradio interface
538
  def create_demo():
539
  with gr.Blocks(css=css, theme=gr.themes.Soft()) as demo:
540
- gr.Markdown(
541
- """
542
- <div class="title">
543
- <h1>✨ Typography Prompt Generator for POD ✨</h1>
544
- </div>
545
- <div class="subtitle">
546
- Generate creative typography prompts for t-shirts & hoodies using Gemini 1.5 Pro
547
- </div>
548
- """
549
- )
550
-
551
- with gr.Group(elem_classes="api-key-container"):
552
- api_key = gr.Textbox(
553
- label="Gemini API Key",
554
- placeholder="Enter your Gemini API key here...",
555
- type="password",
556
- info="Your API key is not stored and only used for this session"
557
- )
558
-
559
- quote = gr.Textbox(
560
- label="Your Quote/Text",
561
- placeholder="Enter the quote you want to create typography designs for...",
562
- info="Example: \"hustle mode: activated\"",
563
- lines=2
564
- )
565
-
566
- with gr.Group(elem_classes="style-options"):
567
- gr.Markdown("### Style Selection")
568
-
569
- # Visual style selection cards
570
- style_cards = gr.HTML(create_style_cards())
571
-
572
- # Hidden field to store selected style
573
- style_preference = gr.Textbox(
574
- value="Auto-Detect",
575
- visible=False,
576
- elem_id="style-input"
577
- )
578
-
579
- with gr.Row():
580
- num_prompts = gr.Slider(
581
- minimum=5,
582
- maximum=15,
583
- value=10,
584
- step=1,
585
- label="Number of Prompts",
586
- info="How many unique prompts to generate"
587
- )
588
-
589
- submit_btn = gr.Button("Generate Typography Prompts", variant="primary", size="lg")
590
 
591
- gr.Examples(
592
- examples=example_quotes,
593
- inputs=quote,
594
- label="Example Quotes"
595
- )
596
-
597
- # Output tabs for different sections
598
  with gr.Tabs() as tabs:
599
- with gr.TabItem("Typography Prompts"):
600
- output = gr.Textbox(
601
- label="Generated Typography Prompts",
602
- placeholder="Your creative prompts will appear here...",
603
- lines=15,
604
- elem_classes=["output-box"],
605
- show_copy_button=True
606
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
607
 
608
- with gr.TabItem("Marketing Insights"):
609
- market_output = gr.Textbox(
610
- label="POD Marketing Analysis",
611
- placeholder="Holiday/Event, Niche information, and keywords will appear here...",
612
- lines=12,
613
- elem_classes=["output-box", "market-box"],
614
- show_copy_button=True
615
- )
616
-
617
- # Store the detected style (for reference/analytics)
618
- detected_style = gr.State(value="")
619
-
620
- gr.Markdown(
621
- """
622
- <footer>
623
- Typography Prompt Generator for POD | Perfect for T-shirts & Hoodies | Create unique designs that sell
624
- </footer>
625
- """
626
- )
 
 
 
 
 
 
 
 
627
 
628
- # Handle form submissions
629
  submit_btn.click(
630
- fn=process_quote,
631
- inputs=[api_key, quote, num_prompts, style_preference],
632
- outputs=[output, market_output, detected_style]
633
  )
634
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
635
  return demo
636
 
637
- # Launch the app
 
 
638
  if __name__ == "__main__":
639
  demo = create_demo()
640
  demo.launch()
 
10
  css = """
11
  body {
12
  font-family: 'Poppins', -apple-system, BlinkMacSystemFont, sans-serif;
13
+ #background-color: #fafafa;
14
  }
15
  .container {
16
  max-width: 900px;
 
33
  margin-top: 15px;
34
  font-family: 'Courier New', monospace;
35
  white-space: pre-wrap;
 
 
 
36
  }
37
  footer {
38
  text-align: center;
 
45
  .api-key-container {
46
  padding: 20px;
47
  margin-bottom: 25px;
 
 
48
  }
49
  .prompt-container {
50
  margin-bottom: 20px;
 
64
  }
65
  .examples-container {
66
  margin-top: 15px;
67
+ padding: 10px;
68
+ border-radius: 8px;
69
+ background-color: #f0f0f0;
70
+ }
71
+ .tab-content {
72
+ padding: 20px;
73
+ background-color: #fff;
74
  }
75
  .logo {
76
  text-align: center;
 
80
  max-width: 80px;
81
  height: auto;
82
  }
83
+ .tabs-container {
84
+ margin-top: 20px;
85
+ }
86
  .style-options {
87
  margin-top: 15px;
 
 
 
88
  }
89
  .market-box {
90
  margin-top: 15px;
91
  padding: 15px;
92
+ border: 1px solid #ddd;
93
+ border-radius: 8px;
94
+ background-color: #f9f9f9;
95
  }
96
  .keyword-list {
97
  margin-top: 5px;
98
  font-family: 'Courier New', monospace;
99
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  """
101
 
102
+ # MODIFIED FUNCTION: Generate structured POD marketing content
103
+ def generate_pod_structured_output(api_key, quote, num_design_prompts, ui_selected_style_name="None (Let AI decide)"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  genai.configure(api_key=api_key)
105
 
 
106
  model = genai.GenerativeModel(
107
  model_name="gemini-1.5-pro",
108
  generation_config={
109
+ "temperature": 0.85, # Slightly higher for more variety in more prompts
110
  "top_p": 0.95,
111
  "top_k": 40,
112
+ "max_output_tokens": 3072, # Increased slightly for potentially more prompts
113
  }
114
  )
115
 
116
+ style_for_header_tag = "AUTO"
117
+ style_guidance_for_llm = "AI chooses the best-fitting style automatically"
 
 
 
 
 
 
 
 
118
 
119
+ if ui_selected_style_name and ui_selected_style_name != "None (Let AI decide)":
120
+ style_for_header_tag = ui_selected_style_name
121
+ style_guidance_for_llm = f"The user has selected a preferred style: '{ui_selected_style_name}'. You MUST apply this style to all relevant prompts in Section 4."
122
+ else:
123
+ style_guidance_for_llm = "The user has left the style choice empty. AI should choose the best-fitting style automatically for Section 4."
 
 
124
 
125
+ # The 'num_design_prompts' now dictates the number of ideas in Section 4
126
+ system_prompt = f"""You are a smart Print-on-Demand (POD) marketing assistant.
127
+ Your task is to analyze the quote "{quote}" and return FOUR structured sections that help with marketing, audience targeting, and design execution.
128
 
129
+ The system has an OPTIONAL STYLES section.
130
+ {style_guidance_for_llm}
131
+ ---
132
+ Return only the following four sections:
133
 
134
+ πŸ“ˆ 1. AMAZON SEARCH KEYWORDS
135
+ Generate a comma-separated list of 15-25 SEO-rich keywords relevant to the quote "{quote}".
136
+ - Mix of broad and niche-specific terms
137
+ - Focus on what real buyers would search for on Amazon
138
+ Example: dog lover shirt, funny pet shirt, dog humor tee, running with dog
139
 
140
+ ---
141
+ πŸ“… 2. HOLIDAYS / EVENTS / NICHES
142
+ Identify all relevant holidays, occasions, events, and niche categories the quote "{quote}" fits into. (Provide at least 3-5 items)
143
+ - Be specific and include both evergreen and seasonal contexts
144
+ Example: Dog Lovers, Pet Adoption Month, National Dog Day, Funny Workout Shirts
145
 
146
+ ---
147
+ 🎯 3. TARGET AUDIENCE
148
+ Describe the ideal customer this quote/design ("{quote}") would appeal to.
149
+ - Include: age range, gender, interests, lifestyle traits, tone preference
150
+ Example: Adults 25–45, pet lovers, dog moms, people with sarcastic or playful humor, casual wearers
151
 
152
+ ---
153
+ 🎨 4. PROMPTS ({num_design_prompts} Ideas) β€” STYLE: [{style_for_header_tag}]
154
+ Generate exactly {num_design_prompts} creative design prompts for the quote: "{quote}", based on:
155
+ - Tone and meaning of the quote
156
+ - The style determined above (either user-selected '{style_for_header_tag if style_for_header_tag != "AUTO" else "AI Choice"}' or AI-recommended if AUTO)
157
+ - Each prompt should include layout, typography style, design elements, and color ideas.
158
+ If a style was provided by the user, apply it consistently.
159
+ If no style was provided (AUTO), analyze the quote and recommend and apply a fitting visual style.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
 
161
+ Example (If User Selected Style = Silhouette and num_design_prompts = 1):
162
+ "1. Bold, stacked text in athletic font for THE PROVIDED QUOTE. A silhouette of a dog pulling a leash with a person chasing behind. High contrast black-and-white design optimized for light shirts."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
 
164
+ Example (If Style = AUTO, AI chose Retro, and num_design_prompts = 1):
165
+ "1. Retro block lettering in faded orange and teal for THE PROVIDED QUOTE. Text arches above a cartoon dog flexing with sunglasses. Distressed texture for a vintage look."
166
+ ---
167
+ ⚠️ Output Format Guidelines:
168
+ - Use headers exactly as shown (including the emojis, numbering, and the dynamic prompt count in Section 4 header).
169
+ - For Section 4, number each design prompt idea starting from 1.
170
+ - Do NOT restate the provided quote "{quote}" unless it's within the design prompt itself where it's part of the design concept.
171
+ - Do NOT explain your reasoning outside the requested sections.
172
+ - Keep your output clean and implementation-ready, directly providing the content under each header.
173
+ - Ensure each section is clearly delineated by the headers and newlines.
174
+ """
175
+ print("--- SYSTEM PROMPT BEING SENT TO API (First 500 chars) ---")
176
+ print(system_prompt[:500] + "...")
177
+ print("--- END OF SYSTEM PROMPT PREVIEW ---")
178
 
179
+ try:
180
+ print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] Attempting to generate {num_design_prompts} prompts for quote: '{quote}' with style: '{ui_selected_style_name}'")
181
+ response = model.generate_content([system_prompt])
182
+ print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] Received response from API.")
183
+ return response.text, None
184
+ except Exception as e:
185
+ error_message = f"Error during API call: {str(e)}"
186
+ print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] {error_message}")
187
+ if hasattr(e, 'message'):
188
+ error_message = f"API Error: {e.message}"
189
+ return None, error_message
190
 
 
191
 
192
+ # MODIFIED PARSING FUNCTION for the structured output
193
+ def parse_structured_output(text_output):
194
+ data = {
195
+ "keywords_header": "πŸ“ˆ 1. AMAZON SEARCH KEYWORDS",
196
+ "keywords_content": "Not found or parsing error.",
197
+ "holidays_header": "πŸ“… 2. HOLIDAYS / EVENTS / NICHES",
198
+ "holidays_content": "Not found or parsing error.",
199
+ "audience_header": "🎯 3. TARGET AUDIENCE",
200
+ "audience_content": "Not found or parsing error.",
201
+ "prompts_header": "🎨 4. PROMPTS β€” STYLE: N/A", # Default, will be replaced
202
+ "prompts_content": "Not found or parsing error."
203
+ }
204
 
205
+ kw_match = re.search(r"(πŸ“ˆ 1\. AMAZON SEARCH KEYWORDS)\s*\n(.*?)(?=\n\nπŸ“… 2\. HOLIDAYS / EVENTS / NICHES|\Z)", text_output, re.DOTALL)
206
+ if kw_match:
207
+ data["keywords_header"] = kw_match.group(1).strip()
208
+ data["keywords_content"] = kw_match.group(2).strip()
209
 
210
+ hol_match = re.search(r"(πŸ“… 2\. HOLIDAYS / EVENTS / NICHES)\s*\n(.*?)(?=\n\n🎯 3\. TARGET AUDIENCE|\Z)", text_output, re.DOTALL)
211
+ if hol_match:
212
+ data["holidays_header"] = hol_match.group(1).strip()
213
+ data["holidays_content"] = hol_match.group(2).strip()
214
 
215
+ # Lookahead for prompts section needs to be dynamic or very general
216
+ aud_match = re.search(r"(🎯 3\. TARGET AUDIENCE)\s*\n(.*?)(?=\n\n🎨 4\. PROMPTS \(\d+ Ideas?\) β€” STYLE:|\Z)", text_output, re.DOTALL)
217
+ if aud_match:
218
+ data["audience_header"] = aud_match.group(1).strip()
219
+ data["audience_content"] = aud_match.group(2).strip()
220
+
221
+ # Regex for prompts header is now more general to capture the number of ideas
222
+ # It matches "🎨 4. PROMPTS (N Ideas) β€” STYLE: [STYLE]"
223
+ prompt_header_pattern_match = re.search(r"(🎨 4\. PROMPTS \(\d+\s*Ideas?\) β€” STYLE:.*?)\s*\n(.*?)\Z", text_output, re.DOTALL)
224
+ if prompt_header_pattern_match:
225
+ data["prompts_header"] = prompt_header_pattern_match.group(1).strip()
226
+ data["prompts_content"] = prompt_header_pattern_match.group(2).strip()
227
+ else:
228
+ # Fallback if the "(N Ideas)" part is missing for some reason
229
+ prompt_fallback_match = re.search(r"(🎨 4\. PROMPTS β€” STYLE:.*?)\s*\n(.*?)\Z", text_output, re.DOTALL)
230
+ if prompt_fallback_match:
231
+ data["prompts_header"] = prompt_fallback_match.group(1).strip()
232
+ data["prompts_content"] = prompt_fallback_match.group(2).strip()
233
+ print("Warning: Parsed prompts using fallback regex. Header might not exactly match '(N Ideas)'.")
234
+
235
+ return data
236
 
 
 
237
 
238
+ # Function to analyze quote sentiment and suggest styles
239
+ def analyze_quote(api_key, quote, progress=gr.Progress()):
240
+ if not api_key: return "Please enter your Gemini API key for analysis."
241
+ if not quote: return "Please enter a quote to analyze."
 
 
 
 
 
 
 
 
 
 
 
 
242
 
 
243
  genai.configure(api_key=api_key)
 
 
244
  model = genai.GenerativeModel(
245
  model_name="gemini-1.5-pro",
246
+ generation_config={"temperature": 0.7, "top_p": 0.95, "max_output_tokens": 1000}
 
 
 
 
247
  )
248
+ system_prompt = f"""Analyze the following quote: "{quote}"
249
+ Provide a brief analysis of:
250
+ 1. The overall mood and sentiment of the quote
251
+ 2. The most appropriate typography styles for this quote
252
+ 3. Suggested color palettes that would complement the meaning
253
+ 4. Best type of decorative elements to include
254
+ Format your response as a concise paragraph for each point."""
 
 
 
 
 
 
 
 
 
255
  try:
 
256
  response = model.generate_content([system_prompt])
257
+ return response.text
 
 
 
 
 
 
 
 
 
 
 
258
  except Exception as e:
259
+ return f"Error analyzing quote: {str(e)}"
260
 
261
+ # MODIFIED Function to handle form submission
262
+ def process_quote(api_key, quote, num_prompts_from_slider, ui_style_preference_str, progress=gr.Progress()):
263
+ if not api_key: return "Please enter your Gemini API key.", "API key is required."
264
+ if not quote: return "Please enter a quote.", "A quote is required."
265
 
266
+ # Ensure num_prompts_from_slider is an int for the system prompt
267
+ try:
268
+ num_design_prompts = int(num_prompts_from_slider)
269
+ except ValueError:
270
+ return "Invalid number of prompts selected.", "Error with prompt count."
271
+
272
  progress(0, desc="Initializing...")
273
+ time.sleep(0.1)
274
 
275
+ progress(0.2, desc=f"Generating {num_design_prompts} design prompts & marketing content...")
 
276
 
277
+ # Pass num_design_prompts to the generation function
278
+ raw_llm_output, error = generate_pod_structured_output(api_key, quote, num_design_prompts, ui_style_preference_str)
 
 
 
 
 
 
 
 
 
279
 
280
  if error:
281
+ return f"Error from API: {error}", f"Marketing insights could not be retrieved. Details: {error}"
282
+ if not raw_llm_output:
283
+ return "Received no content from API.", "Received no content for marketing insights from API."
284
+
285
+ progress(0.7, desc="Parsing API output...")
286
+ parsed_data = parse_structured_output(raw_llm_output)
 
287
 
288
+ market_info_text = (
289
+ f"{parsed_data['keywords_header']}\n{parsed_data['keywords_content']}\n\n"
290
+ f"{parsed_data['holidays_header']}\n{parsed_data['holidays_content']}\n\n"
291
+ f"{parsed_data['audience_header']}\n{parsed_data['audience_content']}"
292
+ )
293
 
294
+ prompts_text = f"{parsed_data['prompts_header']}\n{parsed_data['prompts_content']}"
 
 
 
 
295
 
296
+ if "Not found or parsing error." in prompts_text and "Not found or parsing error." in market_info_text:
297
+ error_message = "Could not fully parse the API output. This might indicate an unexpected response format from the AI."
298
+ raw_output_preview = raw_llm_output[:1000] + "..." if len(raw_llm_output) > 1000 else raw_llm_output
299
+ prompts_text = error_message + "\n\nRaw API Output (preview):\n" + raw_output_preview
300
+ market_info_text = "See main output area for details on parsing failure."
301
 
302
  progress(1.0, desc="Done!")
303
+ return prompts_text, market_info_text
304
 
 
305
  example_quotes = [
306
+ "good vibes only", "dream big, sparkle more", "best mom ever",
307
+ "adventure awaits", "but first, coffee", "fueled by caffeine and chaos",
308
+ "stay wild moon child"
 
 
 
309
  ]
310
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
311
  def create_demo():
312
  with gr.Blocks(css=css, theme=gr.themes.Soft()) as demo:
313
+ gr.Markdown("""
314
+ <div class="title"><h1>✨ POD Marketing & Prompt Generator ✨</h1></div>
315
+ <div class="subtitle">Generate Amazon keywords, target audiences, niches, and design prompts for your quotes using Gemini 1.5 Pro</div>
316
+ """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
317
 
 
 
 
 
 
 
 
318
  with gr.Tabs() as tabs:
319
+ with gr.TabItem("Generate Content"):
320
+ with gr.Group(elem_classes="api-key-container"):
321
+ api_key_input = gr.Textbox(label="Gemini API Key", placeholder="Enter your Gemini API key here...", type="password", info="Your API key is not stored and only used for this session")
322
+ quote_input = gr.Textbox(label="Your Quote/Text", placeholder="Enter the quote you want to analyze...", info="Example: \"good vibes only\"", lines=2)
323
+
324
+ with gr.Group(elem_classes="style-options"):
325
+ gr.Markdown("### Style Preferences for Design Prompts (Section 4)")
326
+ with gr.Row():
327
+ # num_prompts_slider now directly controls the number of design ideas
328
+ num_prompts_slider = gr.Slider(
329
+ minimum=5, maximum=15, value=7, step=1, # Default to 7
330
+ label="Number of Design Ideas for Section 4",
331
+ info="Select how many design prompts to generate for Section 4."
332
+ )
333
+ style_preference_radio = gr.Radio(
334
+ ["None (Let AI decide)", "Retro 70s", "Modern Minimal", "Hand-drawn", "Feminine/Girly", "Bold Experimental", "Silhouette", "Cartoon", "Vintage"],
335
+ label="Preferred Style for Design Prompts", value="None (Let AI decide)", info="Optional: Choose a preferred style for Section 4 design prompts."
336
+ )
337
+
338
+ submit_btn = gr.Button("Generate Marketing Content & Prompts", variant="primary", size="lg")
339
+ gr.Examples(examples=example_quotes, inputs=quote_input, label="Example Quotes")
340
+
341
+ prompts_output_textbox = gr.Textbox(label="🎨 Design Prompts (Section 4)", placeholder="Creative design prompts will appear here...", lines=15, elem_classes=["output-box"], show_copy_button=True)
342
+ marketing_insights_textbox = gr.Textbox(label="πŸ“ˆπŸ“…πŸŽ― Marketing Insights (Sections 1-3)", placeholder="Amazon Keywords, Holidays/Niches, and Target Audience will appear here...", lines=15, elem_classes=["output-box", "market-box"], show_copy_button=True)
343
 
344
+ with gr.TabItem("Quote Analysis (Alternative)"):
345
+ with gr.Group(elem_classes="api-key-container"):
346
+ analysis_api_key = gr.Textbox(label="Gemini API Key", placeholder="Enter your Gemini API key here...", type="password", info="Your API key is not stored.")
347
+ analysis_quote_input = gr.Textbox(label="Your Quote/Text", placeholder="Enter a quote to analyze its mood, style, colors...", lines=2)
348
+ analyze_btn = gr.Button("Analyze Quote", variant="primary")
349
+ analysis_output_textbox = gr.Textbox(label="Quote Analysis Details", placeholder="Detailed analysis of mood, style, colors, elements will appear here...", lines=10, elem_classes=["output-box"])
350
+
351
+ with gr.TabItem("Help"):
352
+ gr.Markdown("""
353
+ ### How to Use the POD Marketing & Prompt Generator
354
+ 1. **Get a Gemini API Key**: Visit [Google AI Studio](https://makersuite.google.com/) and generate an API key.
355
+ 2. **Enter Your API Key**: In the "Generate Content" or "Quote Analysis" tab.
356
+ 3. **Enter Your Quote**: The text you want to base your T-shirt design and marketing on.
357
+ 4. **Number of Design Ideas**: Use the slider to choose how many design prompts (5-15) you want for Section 4.
358
+ 5. **Choose Style (Optional)**: Select a preferred visual style for the design prompts in Section 4. If "None" is selected, the AI will choose.
359
+ 6. **Generate Content**: Click "Generate Marketing Content & Prompts".
360
+
361
+ ### Output Sections (Generate Content Tab):
362
+ * **πŸ“ˆ 1. AMAZON SEARCH KEYWORDS**
363
+ * **πŸ“… 2. HOLIDAYS / EVENTS / NICHES**
364
+ * **🎯 3. TARGET AUDIENCE**
365
+ * **🎨 4. PROMPTS ([Number Selected] Ideas) β€” STYLE: [AUTO or SELECTED STYLE]**
366
+ Sections 1, 2, and 3 appear in "Marketing Insights". Section 4 appears in "Design Prompts".
367
+
368
+ ### Quote Analysis Tab
369
+ Offers analysis on: mood, general typography styles, color palettes, decorative elements.
370
+ """)
371
 
 
372
  submit_btn.click(
373
+ fn=process_quote_with_style_mapping,
374
+ inputs=[api_key_input, quote_input, num_prompts_slider, style_preference_radio],
375
+ outputs=[prompts_output_textbox, marketing_insights_textbox]
376
  )
377
+ analyze_btn.click(fn=analyze_quote, inputs=[analysis_api_key, analysis_quote_input], outputs=analysis_output_textbox)
378
+
379
+ gr.HTML("""
380
+ <script>
381
+ function syncApiKeys() {
382
+ const apiKeyInputs = document.querySelectorAll('input[type="password"]');
383
+ if (apiKeyInputs.length < 2) return;
384
+ const mainApiKeyInput = apiKeyInputs[0];
385
+ const otherApiKeyInputs = Array.from(apiKeyInputs).slice(1);
386
+ mainApiKeyInput.addEventListener('input', function(e) {
387
+ otherApiKeyInputs.forEach(otherElem => {
388
+ if (otherElem.value !== e.target.value) {
389
+ otherElem.value = e.target.value;
390
+ otherElem.dispatchEvent(new Event('input', { bubbles: true }));
391
+ }
392
+ });
393
+ });
394
+ otherApiKeyInputs.forEach(otherInput => {
395
+ otherInput.addEventListener('input', function(e) {
396
+ if (mainApiKeyInput.value !== e.target.value) {
397
+ mainApiKeyInput.value = e.target.value;
398
+ mainApiKeyInput.dispatchEvent(new Event('input', { bubbles: true }));
399
+ }
400
+ otherApiKeyInputs.forEach(syncTarget => {
401
+ if (syncTarget !== e.target && syncTarget.value !== e.target.value) {
402
+ syncTarget.value = e.target.value;
403
+ syncTarget.dispatchEvent(new Event('input', { bubbles: true }));
404
+ }
405
+ });
406
+ });
407
+ });
408
+ }
409
+ let attempts = 0;
410
+ function trySync() {
411
+ if (document.querySelectorAll('input[type="password"]').length > 1) {
412
+ syncApiKeys();
413
+ } else if (attempts < 10) {
414
+ attempts++;
415
+ setTimeout(trySync, 500);
416
+ }
417
+ }
418
+ if (typeof NProgress !== 'undefined') {
419
+ const intervalId = setInterval(() => {
420
+ if (!NProgress.isStarted()) {
421
+ clearInterval(intervalId);
422
+ trySync();
423
+ }
424
+ }, 100);
425
+ } else {
426
+ document.addEventListener('DOMContentLoaded', () => {
427
+ setTimeout(trySync, 1000);
428
+ });
429
+ }
430
+ </script>
431
+ """, visible=False)
432
+ gr.Markdown("<footer>POD Marketing & Prompt Generator | Powered by Gemini AI</footer>")
433
  return demo
434
 
435
+ def process_quote_with_style_mapping(api_key, quote, num_prompts, ui_style_preference_str, progress=gr.Progress()):
436
+ return process_quote(api_key, quote, num_prompts, ui_style_preference_str, progress)
437
+
438
  if __name__ == "__main__":
439
  demo = create_demo()
440
  demo.launch()