File size: 29,186 Bytes
122a610
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5a17ae6
122a610
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5a17ae6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122a610
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5a17ae6
 
122a610
 
 
5a17ae6
 
122a610
 
 
 
 
 
 
 
 
 
5a17ae6
 
 
122a610
5a17ae6
 
 
 
 
 
 
 
 
 
 
 
 
122a610
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5a17ae6
 
 
 
 
 
 
 
 
 
 
 
122a610
 
 
 
 
5a17ae6
122a610
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5a17ae6
 
 
 
 
 
 
 
 
 
 
 
122a610
 
 
 
 
5a17ae6
122a610
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5a17ae6
 
122a610
5a17ae6
 
122a610
 
 
 
 
5a17ae6
122a610
 
 
5a17ae6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
import gradio as gr
import random
from typing import Dict, Any, List
import json
import datetime

class HyperRealisticConfig:
    ETHNICITIES = [
        "Mediterranean", "Scandinavian", "Slavic", "Latin American",
        "Middle Eastern", "East Asian", "South Asian", "African",
        "Caribbean", "Polynesian", "Mixed Heritage"
    ]

    BODY_TYPES = [
        "athletic slender", "voluptuous curves", "petite feminine",
        "toned hourglass", "soft natural", "elegant tall"
    ]

    SKIN_DETAILS = [
        "with subtle freckles and skin texture", "with dewy natural complexion",
        "showing realistic skin pores and veins", "with natural skin imperfections",
        "with subsurface scattering effect", "with delicate skin translucency"
    ]

    PROFESSIONAL_ROLES = [
        {
            "role": "Executive Secretary",
            "uniform": "form-fitting tailored black dress with silk blouse slightly unbuttoned",
            "environment": "luxurious corporate office with marble floors",
            "accessories": "slim tablet and leather portfolio",
            "pose": "bending over conference table arranging documents"
        },
        {
            "role": "Luxury Hotel Manager",
            "uniform": "elegant navy blue blazer and pencil skirt riding up when seated",
            "environment": "five-star hotel lobby with golden lighting",
            "accessories": "gold name tag and master key cards",
            "pose": "leaning forward to assist guest at reception"
        },
        {
            "role": "Fashion Boutique Manager",
            "uniform": "chic black designer dress with elegant draping that accents curves",
            "environment": "high-end boutique with minimalist decor and spotlights",
            "accessories": "designer handbag and inventory clipboard",
            "pose": "kneeling to adjust mannequin display"
        },
        {
            "role": "Corporate Lawyer",
            "uniform": "sharp charcoal gray pantsuit with skirt hiking up when walking",
            "environment": "modern law firm with floor-to-ceiling windows",
            "accessories": "leather briefcase and legal documents",
            "pose": "stretching to reach high shelf in law library"
        },
        {
            "role": "Private Jet Attendant",
            "uniform": "custom-fitted aviation uniform with skirt shortening when bending",
            "environment": "luxurious private jet cabin with leather seats",
            "accessories": "champagne flute and service tray",
            "pose": "bending to serve passengers in low cabin"
        },
        {
            "role": "Art Gallery Curator",
            "uniform": "sleek black turtleneck and high-waisted trousers with visible panty lines",
            "environment": "contemporary art gallery with dramatic lighting",
            "accessories": "curator's catalog and white gloves",
            "pose": "squatting to examine lower artwork"
        },
        {
            "role": "University Professor",
            "uniform": "elegant tweed blazer and knee-length skirt that rides up when seated",
            "environment": "university library with wooden ladders",
            "accessories": "academic books and reading glasses",
            "pose": "reaching for book on high shelf"
        },
        {
            "role": "Ballet Instructor",
            "uniform": "black leotard with wrap skirt and visible dance belt lines",
            "environment": "dance studio with wall mirrors and barre",
            "accessories": "pointe shoe ribbon and demonstration stick",
            "pose": "demonstrating deep plié at barre"
        },
        {
            "role": "Yacht Stewardess",
            "uniform": "crisp white polo and navy shorts that tighten when bending",
            "environment": "luxury yacht deck at sunset with teak flooring",
            "accessories": "silver tray with cocktails and nautical rope",
            "pose": "leaning over side to adjust fenders"
        },
        {
            "role": "Elegant Maid",
            "uniform": "classic black maid dress with white lace apron that flips up",
            "environment": "opulent mansion hallway with grand staircase",
            "accessories": "feather duster and cleaning cart",
            "pose": "bending to polish banister"
        }
    ]

    EVERYDAY_MOMENTS = [
        {
            "scene": "Morning Kitchen",
            "action": "bending down to take something from the low oven causing shirt to rise",
            "outfit": "oversized white t-shirt and cotton shorts with visible panty lines",
            "setting": "sunlit kitchen with open window and morning light",
            "accessories": "coffee mug on counter, apron hanging, fresh herbs",
            "pose": "natural bending with back arched"
        },
        {
            "scene": "Laundry Room",
            "action": "bending over to take clothes from the dryer showing waistband",
            "outfit": "sports top and fitted leggings with subtle sheer panels",
            "setting": "laundry room with baskets and folding table",
            "accessories": "basket of clean clothes, fabric softener",
            "pose": "deep forward bend with legs straight"
        },
        {
            "scene": "Gardening",
            "action": "kneeling while planting flowers with shorts riding up",
            "outfit": "light cotton sundress with thin straps and no bra lines",
            "setting": "backyard garden with flower pots and watering system",
            "accessories": "gardening gloves, watering can, trowel",
            "pose": "kneeling with one leg forward"
        },
        {
            "scene": "Home Yoga",
            "action": "doing downward dog pose with leggings becoming translucent",
            "outfit": "yoga leggings and crop top with sweat patches",
            "setting": "living room with yoga mat and natural side lighting",
            "accessories": "yoga block, water bottle, meditation app",
            "pose": "inverted V-shape with head down"
        },
        {
            "scene": "Shelf Cleaning",
            "action": "stretching on tiptoes to reach a book showing midriff",
            "outfit": "loose t-shirt and pajama shorts with lace trim visible",
            "setting": "personal library with small ladder and dust particles",
            "accessories": "feather duster, stack of books, reading glasses",
            "pose": "full body stretch on toes"
        },
        {
            "scene": "Dog Walk",
            "action": "bending down to attach the leash with dress gaping",
            "outfit": "floral summer dress with thin fabric blowing in wind",
            "setting": "park at sunset with long shadows and golden hour",
            "accessories": "leash, treat pouch, dog water bottle",
            "pose": "squatting while holding excited dog"
        },
        {
            "scene": "Terrace Coffee",
            "action": "sitting with legs crossed, dropping a napkin and reaching",
            "outfit": "silk blouse and pleated skirt that opens when seated",
            "setting": "terrace with iron table and morning mist",
            "accessories": "open book, tea cup, croissant",
            "pose": "leaning forward from seated position"
        },
        {
            "scene": "Climbing Stairs",
            "action": "going up with shopping bags showing back view",
            "outfit": "pencil skirt and fitted blouse with tension lines",
            "setting": "spiral staircase in apartment with dramatic lighting",
            "accessories": "shopping bags, keys in hand, mail",
            "pose": "mid-step with weight on one leg"
        },
        {
            "scene": "Changing Lightbulb",
            "action": "standing on a chair with arms raised fully",
            "outfit": "long t-shirt as dress with back lift when reaching",
            "setting": "kitchen with pendant light and afternoon shadows",
            "accessories": "new lightbulb, step stool, tool box",
            "pose": "tiptoes on chair reaching upward"
        },
        {
            "scene": "Post-Shower",
            "action": "drying hair with towel that keeps slipping",
            "outfit": "short towel wrapped tightly with damp edges",
            "setting": "bathroom with foggy mirror and steam effect",
            "accessories": "hairdryer, bathrobe on hook, skincare products",
            "pose": "bent forward hair drying motion"
        }
    ]

    LACE_BIKINI_STYLES = [
        "delicate black lace bikini with floral embroidery and sheer panels",
        "sheer nude illusion bikini with scalloped lace edges and ribbon ties",
        "burgundy silk bikini with Chantilly lace inserts and satin strings",
        "ivory French lace bikini with pearl accents and delicate stitching",
        "champagne colored lace bikini with geometric patterns and silk backing",
        "deep emerald lace bikini with velvet trim and adjustable sides",
        "rose gold metallic lace bikini with transparent mesh panels",
        "midnight blue lace bikini with crystal beading and underwire support"
    ]

    HOSIERY_STYLES = [
        "black sheer thigh-high stockings with lace tops and stay-up silicone",
        "nude ultra-sheer stockings with reinforced toes and back seam",
        "fishnet thigh-highs with delicate diamond pattern and satin band",
        "back-seam stockings with Cuban heel and vintage reinforcement",
        "sheer to waist stockings with lace panel and no panty line",
        "opaque tights with subtle sheen and reinforced gusset",
        "stay-up stockings with French lace band and bow details",
        "glossy finish stockings with sandalwood foot and sheer legs"
    ]
    
    HEEL_STYLES = [
        "black patent leather stilettos with pointed toe and slim heel",
        "nude pumps with platform and ankle strap for stability",
        "gold strappy sandals with multiple thin straps and high arch",
        "black suede pointed-toe heels with cut-out details",
        "red bottom Louboutin-style heels with glossy finish",
        "clear PVC heels with geometric shapes and metallic accents",
        "silver glitter block heels with ankle support and comfort pad",
        "white leather slingbacks with kitten heel and bow detail"
    ]

    HAIRSTYLES = [
        "long wavy hair with natural volume and soft highlights",
        "sleek straight hair with middle part and glossy finish",
        "elegant updo with loose tendrils and pearl pins",
        "beach waves with sun-kissed highlights and textured ends",
        "high ponytail with smooth finish and subtle curls",
        "braided crown with floral accents and soft flyaways",
        "vintage Hollywood curls with deep side part",
        "messy bun with face-framing strands and natural texture"
    ]

    MAKEUP_STYLES = [
        "natural glam with dewy skin, soft contour, and nude lips",
        "smoky eye with winged liner and voluminous lashes",
        "rosy cheeks with glossy lips and subtle highlighter",
        "bold red lip with flawless matte foundation",
        "bronzed goddess with shimmery eyeshadow and golden highlights",
        "minimalist makeup with tinted moisturizer and mascara",
        "vintage pin-up with defined brows and classic red lip",
        "ethereal glow with iridescent highlighter and soft blush"
    ]

    LIGHTING_DETAILS = [
        "soft diffused lighting with a warm golden glow",
        "dramatic chiaroscuro lighting with deep shadows",
        "natural sunlight filtering through sheer curtains",
        "studio lighting with a three-point setup for dimensionality",
        "candlelit ambiance with flickering shadows",
        "backlit silhouette with a soft halo effect",
        "moody blue-toned lighting for a cinematic feel",
        "sunset glow with lens flare and warm hues"
    ]

    PHOTOGRAPHY_STYLES = [
        "cinematic lighting with rim light and soft shadows",
        "natural window light with lens flare and soft focus",
        "studio softbox lighting with catchlights in eyes",
        "golden hour backlighting with hair light effect",
        "moody low-key lighting with dramatic contrasts",
        "bright high-key lighting with minimal shadows",
        "film noir inspired lighting with venetian blind patterns",
        "ethereal foggy lighting with diffusion filter"
    ]

    CAMERAS = ["Canon EOS R5", "Sony α7R V", "Nikon Z9", "Hasselblad X2D", "Phase One IQ4"]
    LENSES = ["85mm f/1.2", "50mm f/1.4", "24-70mm f/2.8", "100mm f/2.8 macro", "135mm f/1.8"]


class HyperRealisticPromptGenerator:
    def __init__(self):
        self.config = HyperRealisticConfig()
        self.history: List[Dict] = []

    def _get_role_by_name(self, name: str) -> Dict:
        return next((r for r in self.config.PROFESSIONAL_ROLES if r["role"] == name), self.config.PROFESSIONAL_ROLES[0])

    def _get_moment_by_name(self, name: str) -> Dict:
        return next((m for m in self.config.EVERYDAY_MOMENTS if m["scene"] == name), self.config.EVERYDAY_MOMENTS[0])

    def _random_style(self):
        return (
            random.choice(self.config.BODY_TYPES),
            random.choice(self.config.SKIN_DETAILS),
            random.choice(self.config.LACE_BIKINI_STYLES),
            random.choice(self.config.HOSIERY_STYLES),
            random.choice(self.config.HEEL_STYLES),
            random.choice(self.config.HAIRSTYLES),
            random.choice(self.config.MAKEUP_STYLES),
            random.choice(self.config.LIGHTING_DETAILS),
            random.choice(self.config.PHOTOGRAPHY_STYLES),
            random.choice(self.config.CAMERAS),
            random.choice(self.config.LENSES)
        )

    def generate_role_prompt(self, ethnicity: str, role_name: str, nsfw_mode: bool = False) -> str:
        role = self._get_role_by_name(role_name)
        body, skin, lace, hosiery, heels, hairstyle, makeup, lighting, photo_style, camera, lens = self._random_style()

        if nsfw_mode:
            reveal = f"subtle reveal of {lace} through clothing during {role['pose']}"
            underwear_desc = f"wearing {lace} under {role['uniform']}"
            nudity = "artistic sensuality with focus on natural beauty"
        else:
            reveal = f"subtle hint of lace underwear during {role['pose']}"
            underwear_desc = f"with delicate lace underwear under {role['uniform']}"
            nudity = "elegant and professional with subtle sensuality"

        prompt = (
            f"Hyper-realistic 4K photograph, 9:16 vertical composition, full-body portrait from dynamic low angle, "
            f"of a stunning {ethnicity.lower()} {role['role']} with {body} figure {skin}, "
            f"{underwear_desc}, {hosiery}, {heels}, styled with {hairstyle}, and {makeup}. "
            f"In {role['environment']}, captured during {role['pose']} causing {reveal}. "
            f"{lighting}, {photo_style}, Camera: {camera} with {lens} lens, professional lighting setup, "
            f"ultra-high resolution 16K, extreme detail showing skin texture with visible pores, "
            f"realistic fabric wrinkles, subsurface scattering effect, cinematic depth of field. "
            f"Professional atmosphere, elegant composition, {nudity}. "
            f"Include {role['accessories']} in scene. --no explicit_nudity --style raw --stylize 150"
        )

        self.history.append({
            "type": "role",
            "ethnicity": ethnicity,
            "role": role_name,
            "nsfw": nsfw_mode,
            "prompt": prompt,
            "timestamp": datetime.datetime.now().isoformat()
        })
        return prompt

    def generate_moment_prompt(self, ethnicity: str, moment_name: str, nsfw_mode: bool = False) -> str:
        moment = self._get_moment_by_name(moment_name)
        body, skin, lace, hosiery, heels, hairstyle, makeup, lighting, photo_style, camera, lens = self._random_style()

        if nsfw_mode:
            reveal = f"natural reveal of {lace} while {moment['action']}"
            underwear_desc = f"wearing {lace} under {moment['outfit']}"
            sensuality = "artistic intimate moment with sensual atmosphere"
        else:
            reveal = f"subtle suggestion of lace underwear while {moment['action']}"
            underwear_desc = f"with delicate lace underwear under {moment['outfit']}"
            sensuality = "natural private moment with elegant sensuality"

        prompt = (
            f"Hyper-realistic candid photograph, 9:16 vertical format, intimate low angle perspective, "
            f"of a beautiful {ethnicity.lower()} woman with {body} physique {skin}, styled with {hairstyle}, and {makeup}. "
            f"{underwear_desc}, {hosiery}, {heels}, in {moment['setting']}, "
            f"captured during {moment['pose']}, {moment['action']}. "
            f"{lighting}, {photo_style}, Camera: {camera} with {lens}, ultra-high detail 16K resolution, "
            f"realistic skin texture with pores and fine hairs, fabric micro-details, "
            f"natural lighting with soft shadows, cinematic composition. "
            f"{sensuality}. Include {moment['accessories']} in scene. "
            f"--no explicit_content --style photographic --stylize 180"
        )

        self.history.append({
            "type": "moment",
            "ethnicity": ethnicity,
            "scene": moment_name,
            "nsfw": nsfw_mode,
            "prompt": prompt,
            "timestamp": datetime.datetime.now().isoformat()
        })
        return prompt

    def generate_batch_prompts(self, count: int, prompt_type: str, nsfw_mode: bool = False) -> List[str]:
        prompts = []
        for _ in range(count):
            ethnicity = random.choice(self.config.ETHNICITIES)
            if prompt_type == "roles":
                role = random.choice([r["role"] for r in self.config.PROFESSIONAL_ROLES])
                prompts.append(self.generate_role_prompt(ethnicity, role, nsfw_mode))
            else:
                moment = random.choice([m["scene"] for m in self.config.EVERYDAY_MOMENTS])
                prompts.append(self.generate_moment_prompt(ethnicity, moment, nsfw_mode))
        return prompts

    def get_history(self) -> List[Dict]:
        return self.history

    def clear_history(self) -> str:
        self.history = []
        return "History cleared"

# Crear instancia del generador
generator = HyperRealisticPromptGenerator()

# JavaScript para copiar al portapapeles
copy_js = """
function copyToClipboard(text) {
    navigator.clipboard.writeText(text).then(function() {
        console.log('Text copied to clipboard');
    }).catch(function(err) {
        console.error('Could not copy text: ', err);
    });
}
"""

def generate_batch_with_blocks(count, prompt_type, nsfw):
    prompts = generator.generate_batch_prompts(count, prompt_type, nsfw)
    blocks_html = ""
    for i, prompt in enumerate(prompts, 1):
        # Escapar comillas dobles para que el string de JavaScript funcione correctamente
        escaped_prompt = prompt.replace('"', '\\"')
        blocks_html += f"""
        <div class="prompt-block">
            <h4>📋 Prompt #{i}:</h4>
            <textarea id="prompt-{i}" style="width: 100%; height: 150px; padding: 10px; border: 1px solid #ddd; border-radius: 5px; font-family: monospace; margin-bottom: 10px;" readonly>{prompt}</textarea>
            <button class="copy-btn" onclick="copyToClipboard(`{escaped_prompt}`)">📋 Copy Prompt #{i}</button>
        </div>
        """
    return prompts, blocks_html

def export_history():
    history = generator.get_history()
    if not history:
        return "No history to export"
    
    filename = f"prompt_history_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
    # Guardar en un archivo temporal para que Gradio lo pueda devolver
    temp_path = f"/tmp/{filename}"
    with open(temp_path, 'w', encoding='utf-8') as f:
        json.dump(history, f, indent=2, ensure_ascii=False)
    
    # Devolver el archivo para descarga. Gradio maneja esto automáticamente si se devuelve un archivo.
    # Sin embargo, la función de Gradio debe estar diseñada para devolver un gr.File.
    # Como la función original devuelve un string, la mantendré así y asumiré que el usuario
    # manejará la descarga del archivo generado en el entorno de Gradio.
    # Para el despliegue en Hugging Face, el archivo se crea en el directorio del Space.
    
    # Para la prueba local, se puede devolver el path. Para Gradio en HF Spaces,
    # es mejor que el usuario acceda al archivo directamente si es necesario.
    # Para simplificar y seguir el código original, devolveré un mensaje de estado.
    return f"History exported to {filename}. Check the Space's file system."

# Interfaz Gradio
with gr.Blocks(title="HyperRealistic Humanized Perfection Generator", theme=gr.themes.Soft(), css="""
    .prompt-block {
        border: 1px solid #e0e0e0;
        border-radius: 10px;
        padding: 15px;
        margin: 10px 0;
        background: white;
        box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    }
    .copy-btn {
        background: #4CAF50;
        color: white;
        border: none;
        padding: 8px 16px;
        border-radius: 5px;
        cursor: pointer;
        margin-top: 10px;
    }
    .copy-btn:hover {
        background: #45a049;
    }
    .tab-content {
        padding: 20px;
    }
    .generated-prompt {
        font-family: monospace;
        background: #f8f9fa;
        padding: 15px;
        border-radius: 8px;
        border: 1px solid #e9ecef;
        margin: 10px 0;
        white-space: pre-wrap;
        word-wrap: break-word;
    }
""") as demo:
    
    gr.Markdown("# 🌟 HyperRealistic Humanized Perfection Generator")
    gr.Markdown("Generate ultra-realistic humanized prompts with perfect lace details in 9:16 vertical format")
    
    # Inyectar JavaScript
    gr.HTML(f"<script>{copy_js}</script>")
    
    with gr.Tab("👔 Professional Roles"):
        with gr.Row():
            with gr.Column(scale=1):
                role_ethnicity = gr.Dropdown(
                    choices=generator.config.ETHNICITIES,
                    label="🎭 Ethnicity",
                    value="Mediterranean",
                    interactive=True
                )
                role_selection = gr.Dropdown(
                    choices=[role["role"] for role in generator.config.PROFESSIONAL_ROLES],
                    label="💼 Professional Role",
                    value=generator.config.PROFESSIONAL_ROLES[0]["role"],
                    interactive=True
                )
                role_nsfw = gr.Checkbox(
                    label="🎨 Artistic NSFW Mode",
                    value=False,
                    info="Enable for more artistic sensuality"
                )
                generate_role_btn = gr.Button(
                    "✨ Generate Role Prompt",
                    variant="primary",
                    size="lg"
                )
                
            with gr.Column(scale=2):
                role_output = gr.Textbox(
                    label="📝 Generated Prompt",
                    lines=6,
                    max_lines=10,
                    show_copy_button=True
                )
                role_prompt_block = gr.HTML(label="Prompt Block")
        
        # Uso de .then() para actualizar el bloque HTML después de generar el prompt
        def update_role_prompt_block(prompt):
            # Escapar comillas dobles para el string de JavaScript
            escaped_prompt = prompt.replace('"', '\\"')
            return f"""
            <div class="prompt-block">
                <h4>📋 Prompt Ready to Copy:</h4>
                <div class="generated-prompt">{prompt}</div>
                <button class="copy-btn" onclick="copyToClipboard(`{escaped_prompt}`)">📋 Copy Prompt</button>
            </div>
            """
            
        generate_role_btn.click(
            fn=generator.generate_role_prompt,
            inputs=[role_ethnicity, role_selection, role_nsfw],
            outputs=[role_output]
        ).then(
            fn=update_role_prompt_block,
            inputs=[role_output],
            outputs=[role_prompt_block]
        )
    
    with gr.Tab("🏠 Everyday Moments"):
        with gr.Row():
            with gr.Column(scale=1):
                moment_ethnicity = gr.Dropdown(
                    choices=generator.config.ETHNICITIES,
                    label="🎭 Ethnicity",
                    value="Latin American",
                    interactive=True
                )
                moment_selection = gr.Dropdown(
                    choices=[moment["scene"] for moment in generator.config.EVERYDAY_MOMENTS],
                    label="🌅 Daily Moment",
                    value=generator.config.EVERYDAY_MOMENTS[0]["scene"],
                    interactive=True
                )
                moment_nsfw = gr.Checkbox(
                    label="🎨 Artistic NSFW Mode",
                    value=False,
                    info="Enable for more artistic sensuality"
                )
                generate_moment_btn = gr.Button(
                    "✨ Generate Moment Prompt",
                    variant="primary",
                    size="lg"
                )
                
            with gr.Column(scale=2):
                moment_output = gr.Textbox(
                    label="📝 Generated Prompt",
                    lines=6,
                    max_lines=10,
                    show_copy_button=True
                )
                moment_prompt_block = gr.HTML(label="Prompt Block")
        
        # Uso de .then() para actualizar el bloque HTML después de generar el prompt
        def update_moment_prompt_block(prompt):
            # Escapar comillas dobles para el string de JavaScript
            escaped_prompt = prompt.replace('"', '\\"')
            return f"""
            <div class="prompt-block">
                <h4>📋 Prompt Ready to Copy:</h4>
                <div class="generated-prompt">{prompt}</div>
                <button class="copy-btn" onclick="copyToClipboard(`{escaped_prompt}`)">📋 Copy Prompt</button>
            </div>
            """
            
        generate_moment_btn.click(
            fn=generator.generate_moment_prompt,
            inputs=[moment_ethnicity, moment_selection, moment_nsfw],
            outputs=[moment_output]
        ).then(
            fn=update_moment_prompt_block,
            inputs=[moment_output],
            outputs=[moment_prompt_block]
        )
    
    with gr.Tab("🔄 Batch Generator"):
        with gr.Row():
            with gr.Column():
                batch_count = gr.Slider(
                    minimum=1,
                    maximum=10,
                    value=3,
                    step=1,
                    label="Number of Prompts"
                )
                batch_type = gr.Radio(
                    choices=["roles", "moments"],
                    label="Prompt Type",
                    value="roles",
                    interactive=True
                )
                batch_nsfw = gr.Checkbox(
                    label="🎨 Artistic NSFW Mode",
                    value=False
                )
                generate_batch_btn = gr.Button(
                    "🔄 Generate Batch",
                    variant="primary"
                )
                
            with gr.Column():
                batch_output = gr.JSON(
                    label="📦 Batch Prompts"
                )
                batch_prompt_blocks = gr.HTML(label="Batch Prompt Blocks")
        
        generate_batch_btn.click(
            fn=generate_batch_with_blocks,
            inputs=[batch_count, batch_type, batch_nsfw],
            outputs=[batch_output, batch_prompt_blocks]
        )
    
    with gr.Tab("📊 History & Export"):
        with gr.Row():
            with gr.Column():
                history_display = gr.JSON(
                    label="📜 Prompt History",
                    value=generator.get_history
                )
                clear_history_btn = gr.Button(
                    "🗑️ Clear History",
                    variant="secondary"
                )
                
            with gr.Column():
                export_btn = gr.Button(
                    "💾 Export History as JSON",
                    variant="primary"
                )
                export_status = gr.Textbox(
                    label="Export Status",
                    interactive=False
                )
        
        clear_history_btn.click(
            fn=generator.clear_history,
            inputs=[],
            outputs=[export_status]
        ).then(
            fn=generator.get_history,
            inputs=[],
            outputs=[history_display]
        )
        
        export_btn.click(
            fn=export_history,
            inputs=[],
            outputs=[export_status]
        )
        
    demo.launch()