DawnC commited on
Commit
33bf1a9
·
verified ·
1 Parent(s): 9a5066f

Update scene_templates.py

Browse files
Files changed (1) hide show
  1. scene_templates.py +333 -652
scene_templates.py CHANGED
@@ -1,620 +1,297 @@
1
  import logging
2
- from dataclasses import dataclass, field
3
  from typing import Dict, List, Optional
 
4
 
5
  logger = logging.getLogger(__name__)
6
 
7
-
8
  @dataclass
9
- class InpaintingTemplate:
10
- """Data class representing an inpainting template."""
11
-
12
  key: str
13
  name: str
 
 
14
  category: str
15
  icon: str
16
- description: str
17
-
18
- # Prompt templates
19
- prompt_template: str
20
- negative_prompt: str
21
-
22
- # Recommended parameters
23
- controlnet_conditioning_scale: float = 0.7
24
- feather_radius: int = 8
25
  guidance_scale: float = 7.5
26
- num_inference_steps: int = 25
27
-
28
- # Conditioning type preference
29
- preferred_conditioning: str = "canny" # "canny" or "depth"
30
-
31
- # Difficulty level for UI display
32
- difficulty: str = "medium" # "easy", "medium", "advanced"
33
-
34
- # Tips for users
35
- usage_tips: List[str] = field(default_factory=list)
36
 
37
 
38
- class InpaintingTemplateManager:
39
  """
40
- Manages inpainting templates for various use cases.
41
-
42
- Provides categorized presets optimized for different inpainting scenarios
43
- including object replacement, removal, style transfer, and enhancement.
44
-
45
- Attributes:
46
- TEMPLATES: Dictionary of all available templates
47
- CATEGORIES: List of category names in display order
48
-
49
- Example:
50
- >>> manager = InpaintingTemplateManager()
51
- >>> template = manager.get_template("object_replacement")
52
- >>> print(template.prompt_template)
53
  """
54
 
55
- TEMPLATES: Dict[str, InpaintingTemplate] = {
56
- # Object Replacement Category
57
- "object_replacement": InpaintingTemplate(
58
- key="object_replacement",
59
- name="Object Replacement",
60
- category="Replacement",
61
- icon="🔄",
62
- description="Replace selected objects with new content while preserving context",
63
- prompt_template="{content}, seamlessly integrated into scene, matching lighting and perspective, realistic placement",
64
- negative_prompt=(
65
- "inconsistent lighting, wrong perspective, mismatched colors, "
66
- "visible seams, floating objects, unrealistic placement, original object, "
67
- "poorly integrated, disconnected from scene, keeping original, remnants of original"
68
- ),
69
- controlnet_conditioning_scale=0.32, # Optimized: Lower for clean replacement (was 0.38)
70
- feather_radius=10, # Optimized: Tighter blending (was 14)
71
- guidance_scale=11.0, # Optimized: Higher for object accuracy (was 10.0)
72
- num_inference_steps=10, # Optimized: 10 steps (was 12)
73
- preferred_conditioning="canny", # ✅ KEPT: Edge-based replacement
74
- difficulty="medium",
75
- usage_tips=[
76
- "Draw mask PRECISELY around the object to replace (include small margin)",
77
- "Low conditioning (0.32) helps remove original object completely",
78
- "Be specific: Instead of 'plant', use 'large green potted fern with detailed leaves'",
79
- "📝 Example prompts:",
80
- " • 'elegant white ceramic teacup with delicate gold rim and floral pattern'",
81
- " • 'modern silver laptop computer with sleek metallic finish'",
82
- " • 'vintage wooden desk lamp with warm brass details'"
83
- ]
84
  ),
85
-
86
- "face_swap": InpaintingTemplate(
87
- key="face_swap",
88
- name="Face Enhancement",
89
- category="Replacement",
90
- icon="👤",
91
- description="Enhance or modify facial features - SUBTLE adjustments only (skin, expression, lighting)",
92
- prompt_template="{content}, natural skin texture, proper facial proportions, realistic lighting, detailed facial features",
93
- negative_prompt=(
94
- "deformed face, asymmetric features, unnatural skin, "
95
- "plastic appearance, wrong eye direction, blurry features, "
96
- "artificial smoothing, uncanny valley, distorted proportions"
97
- ),
98
- controlnet_conditioning_scale=0.88, # Very high - preserves facial structure
99
- feather_radius=6,
100
- guidance_scale=8.5,
101
- num_inference_steps=10, # Optimized: 10 steps (was 12)
102
- preferred_conditioning="canny",
103
- difficulty="advanced", # ⚠️ Advanced: Faces are sensitive
104
- usage_tips=[
105
- "⚠️ ADVANCED: Best for SUBTLE enhancements (skin tone, blemishes, expression)",
106
- "NOT for major changes (face shape, eye size, feature positions)",
107
- "Draw mask CAREFULLY around face outline (avoid hair and neck)",
108
- "High conditioning (0.88) preserves facial structure - only small modifications",
109
- "📝 Example prompts:",
110
- " • 'warm friendly smile with bright eyes and natural expression'",
111
- " • 'professional headshot with confident neutral expression, clear smooth skin'",
112
- " • 'gentle smile with soft natural lighting on face'"
113
- ]
114
  ),
115
-
116
- "clothing_change": InpaintingTemplate(
117
- key="clothing_change",
118
- name="Clothing Change",
119
- category="Replacement",
120
- icon="👕",
121
- description="Change clothing color, pattern, style - preserves fabric folds and texture",
122
- prompt_template="CHANGE to {content}, vivid saturated color, natural fabric texture, proper folds and wrinkles, correct fit",
123
- negative_prompt=(
124
- "wrong body proportions, floating fabric, unrealistic wrinkles, "
125
- "mismatched lighting, visible edges, original clothing style, "
126
- "keeping same color, original color, faded colors, unchanged appearance, partial change, "
127
- "black clothing, dark original color"
128
- ),
129
- controlnet_conditioning_scale=0.30, # Optimized: Much lower for color change freedom (was 0.42)
130
- feather_radius=14,
131
- guidance_scale=11.5, # Optimized: Higher for color accuracy (was 10.5)
132
- num_inference_steps=10, # Optimized: 10 steps (was 12)
133
- preferred_conditioning="depth", # ✅ KEPT: Preserves fabric folds and texture
134
- difficulty="easy", # ✅ Easy: Good success rate
135
- usage_tips=[
136
- "✅ BALANCED: Low conditioning (0.30) allows color change while depth preserves folds",
137
- "Works for both moderate and dramatic color changes (black→red now possible!)",
138
- "For extreme changes, be VERY specific with color: 'BRIGHT red', 'PURE white'",
139
- "📝 Example prompts:",
140
- " • 'BRIGHT red polo shirt with vivid saturated color, clean collar and soft fabric'",
141
- " • 'deep navy blue formal blazer with fine texture and professional fit'",
142
- " • 'PURE white dress shirt with crisp clean fabric'"
143
- ]
144
  ),
145
-
146
- "dramatic_color_change": InpaintingTemplate(
147
- key="dramatic_color_change",
148
- name="Dramatic Color Change",
149
- category="Replacement",
150
- icon="🎨",
151
- description="Extreme color transformations (dark↔light, black↔white) - especially for illustrations",
152
- prompt_template="COMPLETELY SOLID {content}, entire area pure color, all fabric same vivid color, no original color remaining",
153
- negative_prompt=(
154
- "original color, keeping same shade, partial change, color bleeding, "
155
- "faded colors, mixed tones, subtle change, gradual transition, "
156
- "original appearance, unchanged, dark remnants, light patches, "
157
- "black clothing, dark colors, keeping original darkness, "
158
- "black fabric, keeping black, original black color, dark body, "
159
- "partial color change, colored trim only, colored edges only, colored outline only, "
160
- "black body with colored decoration, original color body"
161
- ),
162
- controlnet_conditioning_scale=0.15, # Optimized: Extremely low for anime/illustration (was 0.25)
163
- feather_radius=16,
164
- guidance_scale=15.5, # Optimized: Very high for strong color adherence (was 13.5)
165
- num_inference_steps=10, # Optimized: 10 steps sufficient (was 12)
166
- preferred_conditioning="canny", # Optimized: Canny for color freedom (was depth)
167
- difficulty="medium", # ⚠️ Medium: Extreme changes challenging
168
- usage_tips=[
169
- "✅ OPTIMIZED for anime/illustration extreme color changes (black↔white, black↔red)",
170
- "Use COMPLETE descriptors: 'solid PURE WHITE', 'completely BRIGHT RED', 'entire garment JET BLACK'",
171
- "Very low conditioning (0.15) allows complete color transformation on flat-color illustrations",
172
- "Add 'completely', 'solid', 'entire', 'pure' to ensure full coverage",
173
- "📝 Example prompts:",
174
- " • 'solid PURE WHITE dress shirt, completely white fabric'",
175
- " • 'completely JET BLACK leather jacket, entire garment black'",
176
- " • 'solid BRIGHT RED polo shirt, all fabric vivid red, no black'"
177
- ]
178
  ),
179
-
180
- "clothing_addition": InpaintingTemplate(
181
- key="clothing_addition",
182
- name="Add Accessories",
183
- category="Replacement",
184
- icon="👔",
185
- description="⚡ EXPERIMENTAL: Add ties, pockets, buttons to clothing - challenging, may require multiple attempts",
186
- prompt_template="{content}, clearly visible, highly detailed accessory, seamlessly integrated into clothing, proper placement and perspective",
187
- negative_prompt=(
188
- "missing details, incomplete, floating objects, disconnected, "
189
- "unrealistic placement, wrong perspective, blurry, poorly integrated, "
190
- "invisible, faint, unclear, hidden, absent, not visible"
191
- ),
192
- controlnet_conditioning_scale=0.20, # Optimized: Extremely low for object generation (was 0.25)
193
- feather_radius=12,
194
- guidance_scale=15.0, # Optimized: Very high for strong prompt adherence (was 14.0)
195
- num_inference_steps=10, # Optimized: 10 steps (was 12)
196
- preferred_conditioning="canny", # Optimized: Canny for maximum freedom (was depth)
197
- difficulty="advanced", # ⚠️⚠️ ADVANCED: Low success rate, experimental
198
- usage_tips=[
199
- "⚡⚡ EXPERIMENTAL FEATURE: Adding new objects is very challenging",
200
- "⚠️ Lower success rate due to complexity - may require multiple attempts",
201
- "Draw mask from NECK to CHEST (vertical strip) for ties, not just collar area",
202
- "Very low conditioning (0.20) gives model maximum freedom to generate new objects",
203
- "📝 Example prompts:",
204
- " • 'burgundy silk necktie with diagonal stripes and Windsor knot, hanging down from collar to chest'",
205
- " • 'white pocket square with neat fold, visible in breast pocket'",
206
- " • 'silver lapel pin with detailed engraving on left collar'",
207
- "💡 TIP: For ties, mask should cover the entire length where tie should appear"
208
- ]
209
  ),
210
 
211
- # Object Removal Category
212
- "object_removal": InpaintingTemplate(
213
- key="object_removal",
214
- name="Object Removal",
215
- category="Removal",
216
- icon="🗑️",
217
- description="Remove unwanted objects and fill with matching background",
218
- prompt_template="clean background, seamless continuation, {content}",
219
- negative_prompt=(
220
- "visible patches, color mismatch, texture inconsistency, "
221
- "ghost artifacts, blur spots, repeated patterns, visible seams, remnants"
222
- ),
223
- controlnet_conditioning_scale=0.42, # Optimized: Lower for cleaner removal (was 0.48)
224
- feather_radius=14,
225
- guidance_scale=8.5, # Optimized: Higher for accuracy (was 8.0)
226
- num_inference_steps=10, # Optimized: 10 steps (was 12)
227
- preferred_conditioning="canny", # ✅ KEPT: Edge-based removal
228
- difficulty="easy", # ✅ Easy: Inpainting core strength
229
- usage_tips=[
230
- "Draw mask slightly BEYOND object edges for better blending",
231
- "Lower conditioning (0.42) ensures complete removal without traces",
232
- "Describe what background SHOULD look like (e.g., 'grass lawn', 'wooden floor')",
233
- "📝 Example prompts:",
234
- " • 'clean grass lawn with natural green color'",
235
- " • 'smooth wooden floor with consistent grain pattern'",
236
- " • 'plain white wall with even texture'"
237
- ]
238
  ),
239
-
240
- "watermark_removal": InpaintingTemplate(
241
- key="watermark_removal",
242
- name="Watermark Removal",
243
- category="Removal",
244
- icon="💧",
245
- description="Remove watermarks and text overlays",
246
- prompt_template="clean image, no text, seamless background, {content}",
247
- negative_prompt=(
248
- "text, watermark, logo, signature, letters, numbers, visible artifacts, "
249
- "color inconsistency, blur, remnants, ghost text"
250
- ),
251
- controlnet_conditioning_scale=0.40, # Optimized: Lower for complete removal (was 0.45)
252
- feather_radius=12,
253
- guidance_scale=9.0, # Optimized: Higher for clean result (was 8.5)
254
- num_inference_steps=10, # Optimized: 10 steps (was 12)
255
- preferred_conditioning="canny", # ✅ KEPT: Edge-based removal
256
- difficulty="easy", # ✅ Easy: Good success rate
257
- usage_tips=[
258
- "Draw mask covering ALL watermark/text areas precisely",
259
- "Lower conditioning (0.40) ensures watermark disappears completely",
260
- "Describe what SHOULD be there instead (e.g., 'sky', 'fabric texture')",
261
- "📝 Example prompts:",
262
- " • 'clean blue sky with smooth gradient'",
263
- " • 'natural skin texture without marks'",
264
- " • 'smooth fabric surface with consistent color'"
265
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
266
  ),
267
 
268
- "blemish_removal": InpaintingTemplate(
269
- key="blemish_removal",
270
- name="Blemish Removal",
271
- category="Removal",
272
- icon="",
273
- description="Remove skin blemishes, scratches, or small imperfections",
274
- prompt_template="clean smooth surface, natural texture, {content}",
275
- negative_prompt=(
276
- "artificial smoothing, plastic texture, visible editing, "
277
- "color patches, unnatural appearance, over-processed"
278
- ),
279
- controlnet_conditioning_scale=0.6,
280
- feather_radius=6,
281
- guidance_scale=6.5,
282
- num_inference_steps=10, # Optimized: 10 steps (was 12)
283
- preferred_conditioning="canny",
284
- difficulty="easy", # ✅ Easy: Small areas, high success
285
- usage_tips=[
286
- "Draw small precise masks for EACH blemish/imperfection",
287
- "Lower guidance (6.5) preserves natural skin texture",
288
- "📝 Example prompts:",
289
- " 'natural clean skin with smooth texture'",
290
- " 'smooth surface without scratches or marks'",
291
- " 'clear skin with natural pores and texture'"
292
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
293
  ),
294
 
295
- # Style Transfer Category
296
- "style_artistic": InpaintingTemplate(
297
- key="style_artistic",
298
- name="Artistic Style",
299
- category="Style",
 
 
300
  icon="🎨",
301
- description="Apply artistic style to selected region",
302
- prompt_template="{content}, distinctive artistic style, strong painterly effect, creative interpretation, visible brushstrokes",
303
- negative_prompt=(
304
- "photorealistic, plain, boring, low contrast, unchanged, "
305
- "inconsistent style, harsh transitions, original appearance, realistic photo"
306
- ),
307
- controlnet_conditioning_scale=0.42, # Optimized: Lower for style freedom (was 0.52)
308
- feather_radius=12,
309
- guidance_scale=12.5, # Optimized: Higher for style accuracy (was 11.5)
310
- num_inference_steps=10, # Optimized: 10 steps (was 12)
311
- preferred_conditioning="canny", # ✅ KEPT: Edge-based styling
312
- difficulty="medium", # ⚠️ Medium: Style changes need steps
313
- usage_tips=[
314
- "Works best on larger areas (faces, clothing, backgrounds) for visible transformation",
315
- "Lower conditioning (0.42) allows artistic reinterpretation",
316
- "Be VERY specific about art style for best results",
317
- "📝 Example prompts:",
318
- " • 'impressionist oil painting with visible thick brushstrokes and vibrant colors'",
319
- " • 'watercolor painting with soft edges and delicate color washes'",
320
- " • 'Van Gogh style with swirling brushstrokes and bold color contrasts'"
321
- ]
322
  ),
323
-
324
- "style_vintage": InpaintingTemplate(
325
- key="style_vintage",
326
- name="Vintage Look",
327
- category="Style",
328
- icon="📻",
329
- description="Apply vintage or retro aesthetic to selected area",
330
- prompt_template="{content}, strong vintage aesthetic, warm sepia tones, film grain texture, nostalgic atmosphere",
331
- negative_prompt=(
332
- "modern, digital, cold colors, harsh contrast, "
333
- "oversaturated, neon colors, contemporary look, clean digital, crisp"
334
- ),
335
- controlnet_conditioning_scale=0.45, # Optimized: Lower for style freedom (was 0.55)
336
- feather_radius=14,
337
- guidance_scale=11.0, # Optimized: Higher for era accuracy (was 10.5)
338
- num_inference_steps=10, # Optimized: 10 steps (was 12)
339
- preferred_conditioning="depth", # Optimized: Depth for texture/grain (was canny)
340
- difficulty="medium", # ⚠️ Medium: Color/texture adjustments
341
- usage_tips=[
342
- "Works best on medium to large regions for visible aesthetic change",
343
- "Depth conditioning preserves texture while applying vintage look",
344
- "Specify era and style for best results",
345
- "📝 Example prompts:",
346
- " • '1920s sepia photograph with faded brown tones and soft grain'",
347
- " • '1970s vintage photo with warm orange tones and slight film grain'",
348
- " • '1950s Kodachrome with saturated warm colors and nostalgic feel'"
349
- ]
350
  ),
351
-
352
- "style_anime": InpaintingTemplate(
353
- key="style_anime",
354
- name="Anime Style",
355
- category="Style",
356
- icon="🎌",
357
- description="⚡ Transform to anime style - dramatic change, may need multiple attempts",
358
- prompt_template="{content}, anime illustration style, clean sharp lines, vibrant saturated colors, cel-shaded with flat colors",
359
- negative_prompt=(
360
- "photorealistic, blurry lines, muddy colors, realistic photo, "
361
- "3D render, uncanny valley, western cartoon, gradient shading, photographic"
362
- ),
363
- controlnet_conditioning_scale=0.35, # Optimized: Lower for dramatic style change (was 0.48)
364
- feather_radius=10,
365
- guidance_scale=14.0, # Optimized: Higher for style accuracy (was 12.5)
366
- num_inference_steps=10, # Optimized: 10 steps (was 12)
367
- preferred_conditioning="canny",
368
- difficulty="advanced", # ⚠️ Advanced: Extreme style transformation
369
- usage_tips=[
370
- "⚡ DRAMATIC transformation - best for portraits and characters",
371
- "⚠️ Significant stylistic changes require good source image quality",
372
- "Lower conditioning (0.35) allows dramatic style transformation",
373
- "Expect anime-style interpretation, not exact anime conversion",
374
- "📝 Example prompts:",
375
- " • 'modern anime style with large expressive eyes and vibrant colors'",
376
- " • 'Studio Ghibli style with soft features and warm color palette'",
377
- " • 'manga style with clean black lines and cel-shaded coloring'"
378
- ]
379
  ),
380
-
381
- # Detail Enhancement Category
382
- "detail_enhance": InpaintingTemplate(
383
- key="detail_enhance",
384
- name="Detail Enhancement",
385
- category="Enhancement",
386
- icon="🔍",
387
- description="Add fine details and textures to selected area",
388
- prompt_template="{content}, highly detailed, intricate textures, fine details, sharp focus",
389
- negative_prompt=(
390
- "blurry, smooth, low detail, soft focus, "
391
- "oversimplified, lacking texture"
392
- ),
393
- controlnet_conditioning_scale=0.85, # Very high - preserves structure
394
- feather_radius=4,
395
- guidance_scale=8.0,
396
- num_inference_steps=10, # Optimized: 10 steps (was 12)
397
- preferred_conditioning="depth",
398
- difficulty="easy", # ✅ Easy: Enhancement not generation
399
- usage_tips=[
400
- "High conditioning (0.85) preserves overall structure while adding detail",
401
- "Best for adding fine details to existing objects",
402
- "📝 Example prompts:",
403
- " • 'highly detailed fabric with visible weave and fine threads'",
404
- " • 'intricate wood grain with natural knots and detailed texture'",
405
- " • 'sharp facial features with fine skin pores and detail'"
406
- ]
407
  ),
408
 
409
- "texture_add": InpaintingTemplate(
410
- key="texture_add",
411
- name="Texture Addition",
412
- category="Enhancement",
413
- icon="🧱",
414
- description="Add or enhance surface textures",
415
- prompt_template="{content} texture, realistic surface detail, natural material appearance",
416
- negative_prompt=(
417
- "flat, smooth, unrealistic, plastic, "
418
- "wrong material, inconsistent texture"
419
- ),
420
- controlnet_conditioning_scale=0.8, # High - preserves shape
421
- feather_radius=5,
422
- guidance_scale=7.5,
423
- num_inference_steps=10, # Optimized: 10 steps (was 12)
424
- preferred_conditioning="depth",
425
- difficulty="easy", # ✅ Easy: Texture overlay
426
- usage_tips=[
427
- "Specify material type clearly for best results",
428
- "Depth conditioning preserves 3D form while changing texture",
429
- "📝 Example prompts:",
430
- " • 'rough wood texture with natural grain and knots'",
431
- " • 'soft cotton fabric with gentle weave pattern'",
432
- " • 'smooth marble surface with subtle veining'"
433
- ]
434
  ),
435
-
436
- "lighting_fix": InpaintingTemplate(
437
- key="lighting_fix",
438
- name="Lighting Correction",
439
- category="Enhancement",
440
- icon="💡",
441
- description="Correct or enhance lighting in selected area",
442
- prompt_template="{content}, proper lighting, natural shadows, balanced exposure",
443
- negative_prompt=(
444
- "harsh shadows, overexposed, underexposed, "
445
- "flat lighting, unnatural highlights"
446
- ),
447
- controlnet_conditioning_scale=0.65,
448
- feather_radius=15, # Large for smooth transitions
449
- guidance_scale=7.0,
450
- num_inference_steps=10, # Optimized: 10 steps (was 12)
451
- preferred_conditioning="depth",
452
- difficulty="easy", # ✅ Easy: Lighting adjustments
453
- usage_tips=[
454
- "Use large feather (15px) for smooth lighting transitions",
455
- "Best for fixing uneven lighting or adding natural light",
456
- "📝 Example prompts:",
457
- " • 'soft natural lighting from window, gentle shadows'",
458
- " • 'balanced exposure with warm golden hour light'",
459
- " • 'even studio lighting with soft diffused shadows'"
460
- ]
461
  ),
462
-
463
- # Background Category
464
- "background_extend": InpaintingTemplate(
465
- key="background_extend",
466
- name="Background Extension",
467
- category="Background",
468
- icon="📐",
469
- description="Extend image background seamlessly",
470
- prompt_template="seamless background extension, {content}, consistent style and lighting",
471
- negative_prompt=(
472
- "visible seams, style mismatch, lighting inconsistency, "
473
- "repeated elements, unnatural continuation, abrupt changes"
474
- ),
475
- controlnet_conditioning_scale=0.45, # Optimized: Lower for extension freedom (was 0.55)
476
- feather_radius=20, # Largest for smooth blending
477
- guidance_scale=8.5, # Optimized: Higher for consistency (was 8.0)
478
- num_inference_steps=10, # Optimized: 10 steps (was 12)
479
- preferred_conditioning="canny", # ✅ KEPT: Edge-based extension
480
- difficulty="medium", # ⚠️ Medium: Needs consistency
481
- usage_tips=[
482
- "Draw mask on area to extend (edges of image)",
483
- "Large feather (20px) ensures smooth blending with existing background",
484
- "Canny conditioning focuses on edges for seamless continuation",
485
- "📝 Example prompts:",
486
- " • 'continue the wooden floor with same grain pattern'",
487
- " • 'extend blue sky with matching clouds and lighting'",
488
- " • 'seamless continuation of brick wall texture'"
489
- ]
490
  ),
491
-
492
- "background_replace": InpaintingTemplate(
493
- key="background_replace",
494
- name="Background Replacement",
495
- category="Background",
496
- icon="🖼️",
497
- description="Replace background while keeping subject intact",
498
- prompt_template="{content}, professional background scene, seamless integration with subject, matching lighting and atmosphere",
499
- negative_prompt=(
500
- "floating subject, inconsistent lighting, disconnected, "
501
- "wrong perspective, visible edges, color mismatch, original background, "
502
- "poor integration, obvious composite"
503
- ),
504
- controlnet_conditioning_scale=0.48, # Optimized: Lower for background freedom (was 0.60)
505
- feather_radius=12,
506
- guidance_scale=10.5, # Optimized: Higher for scene accuracy (was 9.5)
507
- num_inference_steps=10, # Optimized: 10 steps (was 12)
508
- preferred_conditioning="depth", # ✅ KEPT: Preserves depth relationships
509
- difficulty="medium", # ⚠️ Medium: Large area replacement
510
- usage_tips=[
511
- "Draw mask around ENTIRE background (leave subject unmasked with small margin)",
512
- "Depth conditioning (0.48) preserves subject-background spatial relationship",
513
- "Include lighting description to match subject for natural results",
514
- "📝 Example prompts:",
515
- " • 'professional photography studio with white backdrop and soft lighting'",
516
- " • 'modern minimalist office with white walls and bright natural lighting'",
517
- " • 'sunny beach with blue ocean and golden hour lighting'"
518
- ]
519
  ),
520
  }
521
 
522
  # Category display order
523
- CATEGORIES = ["Replacement", "Removal", "Style", "Enhancement", "Background"]
524
 
525
  def __init__(self):
526
- """Initialize the InpaintingTemplateManager."""
527
- logger.info(f"InpaintingTemplateManager initialized with {len(self.TEMPLATES)} templates")
528
-
529
- def get_all_templates(self) -> Dict[str, InpaintingTemplate]:
530
- """
531
- Get all available templates.
532
 
533
- Returns
534
- -------
535
- dict
536
- Dictionary of all templates keyed by template key
537
- """
538
  return self.TEMPLATES
539
 
540
- def get_template(self, key: str) -> Optional[InpaintingTemplate]:
541
- """
542
- Get a specific template by key.
543
-
544
- Parameters
545
- ----------
546
- key : str
547
- Template identifier
548
-
549
- Returns
550
- -------
551
- InpaintingTemplate or None
552
- Template if found, None otherwise
553
- """
554
  return self.TEMPLATES.get(key)
555
 
556
- def get_templates_by_category(self, category: str) -> List[InpaintingTemplate]:
557
- """
558
- Get all templates in a specific category.
559
-
560
- Parameters
561
- ----------
562
- category : str
563
- Category name
564
-
565
- Returns
566
- -------
567
- list
568
- List of templates in the category
569
- """
570
  return [t for t in self.TEMPLATES.values() if t.category == category]
571
 
572
  def get_categories(self) -> List[str]:
573
- """
574
- Get list of all categories in display order.
575
-
576
- Returns
577
- -------
578
- list
579
- Category names
580
- """
581
  return self.CATEGORIES
582
 
583
  def get_template_choices_sorted(self) -> List[str]:
584
  """
585
  Get template choices formatted for Gradio dropdown.
586
-
587
- Returns list of display strings sorted by category then A-Z.
588
- Format: "icon Name"
589
-
590
- Returns
591
- -------
592
- list
593
- Formatted display strings for dropdown
594
  """
595
  display_list = []
 
 
 
596
 
597
- for category in self.CATEGORIES:
598
- templates = self.get_templates_by_category(category)
599
- for template in sorted(templates, key=lambda t: t.name):
600
- display_name = f"{template.icon} {template.name}"
601
- display_list.append(display_name)
602
-
603
  return display_list
604
 
605
  def get_template_key_from_display(self, display_name: str) -> Optional[str]:
606
  """
607
  Get template key from display name.
608
-
609
- Parameters
610
- ----------
611
- display_name : str
612
- Display string like "🔄 Object Replacement"
613
-
614
- Returns
615
- -------
616
- str or None
617
- Template key if found
618
  """
619
  if not display_name:
620
  return None
@@ -624,102 +301,33 @@ class InpaintingTemplateManager:
624
  return key
625
  return None
626
 
627
- def get_parameters_for_template(self, key: str) -> Dict[str, any]:
628
- """
629
- Get recommended parameters for a template.
630
-
631
- Parameters
632
- ----------
633
- key : str
634
- Template key
635
-
636
- Returns
637
- -------
638
- dict
639
- Dictionary of parameter names and values
640
- """
641
- template = self.get_template(key)
642
- if not template:
643
- return {}
644
-
645
- return {
646
- "controlnet_conditioning_scale": template.controlnet_conditioning_scale,
647
- "feather_radius": template.feather_radius,
648
- "guidance_scale": template.guidance_scale,
649
- "num_inference_steps": template.num_inference_steps,
650
- "preferred_conditioning": template.preferred_conditioning
651
- }
652
-
653
- def build_prompt(self, key: str, content: str) -> str:
654
- """
655
- Build complete prompt from template and user content.
656
-
657
- Parameters
658
- ----------
659
- key : str
660
- Template key
661
- content : str
662
- User-provided content description
663
-
664
- Returns
665
- -------
666
- str
667
- Formatted prompt with content inserted
668
- """
669
  template = self.get_template(key)
670
- if not template:
671
- return content
672
-
673
- return template.prompt_template.format(content=content)
674
-
675
- def get_negative_prompt(self, key: str) -> str:
676
- """
677
- Get negative prompt for a template.
678
-
679
- Parameters
680
- ----------
681
- key : str
682
- Template key
683
-
684
- Returns
685
- -------
686
- str
687
- Negative prompt string
688
- """
689
  template = self.get_template(key)
690
- if not template:
691
- return ""
692
- return template.negative_prompt
693
 
694
- def get_usage_tips(self, key: str) -> List[str]:
695
- """
696
- Get usage tips for a template.
697
-
698
- Parameters
699
- ----------
700
- key : str
701
- Template key
702
-
703
- Returns
704
- -------
705
- list
706
- List of tip strings
707
- """
708
  template = self.get_template(key)
709
- if not template:
710
- return []
711
- return template.usage_tips
712
 
713
  def build_gallery_html(self) -> str:
714
  """
715
- Build HTML for template gallery display.
716
-
717
- Returns
718
- -------
719
- str
720
- HTML string for Gradio display
721
  """
722
- html_parts = ['<div class="inpainting-gallery">']
723
 
724
  for category in self.CATEGORIES:
725
  templates = self.get_templates_by_category(category)
@@ -727,21 +335,94 @@ class InpaintingTemplateManager:
727
  continue
728
 
729
  html_parts.append(f'''
730
- <div class="inpainting-category">
731
- <h4 class="inpainting-category-title">{category}</h4>
732
- <div class="inpainting-grid">
733
  ''')
734
 
735
- for template in sorted(templates, key=lambda t: t.name):
736
  html_parts.append(f'''
737
- <div class="inpainting-card" data-template="{template.key}">
738
- <span class="inpainting-icon">{template.icon}</span>
739
- <span class="inpainting-name">{template.name}</span>
740
- <span class="inpainting-desc">{template.description[:50]}...</span>
741
- </div>
742
  ''')
743
 
744
  html_parts.append('</div></div>')
745
 
746
  html_parts.append('</div>')
747
- return ''.join(html_parts)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import logging
 
2
  from typing import Dict, List, Optional
3
+ from dataclasses import dataclass
4
 
5
  logger = logging.getLogger(__name__)
6
 
 
7
  @dataclass
8
+ class SceneTemplate:
9
+ """Data class representing a scene template"""
 
10
  key: str
11
  name: str
12
+ prompt: str
13
+ negative_extra: str
14
  category: str
15
  icon: str
 
 
 
 
 
 
 
 
 
16
  guidance_scale: float = 7.5
 
 
 
 
 
 
 
 
 
 
17
 
18
 
19
+ class SceneTemplateManager:
20
  """
21
+ Manages curated scene templates for background generation.
22
+ Provides categorized presets that users can select with one click.
 
 
 
 
 
 
 
 
 
 
 
23
  """
24
 
25
+ # Scene template definitions
26
+ TEMPLATES: Dict[str, SceneTemplate] = {
27
+ # Professional Category
28
+ "office_modern": SceneTemplate(
29
+ key="office_modern",
30
+ name="Modern Office",
31
+ prompt="modern minimalist office interior, clean white desk, large floor-to-ceiling windows, natural daylight, professional corporate environment, soft shadows, contemporary furniture",
32
+ negative_extra="messy, cluttered, dark, old",
33
+ category="Professional",
34
+ icon="🏢",
35
+ guidance_scale=7.5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  ),
37
+ "office_executive": SceneTemplate(
38
+ key="office_executive",
39
+ name="Executive Suite",
40
+ prompt="luxurious executive office, mahogany desk, leather chair, city skyline view through windows, warm ambient lighting, bookshelf, elegant professional setting",
41
+ negative_extra="cheap, cramped, messy",
42
+ category="Professional",
43
+ icon="👔",
44
+ guidance_scale=7.5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  ),
46
+ "studio_white": SceneTemplate(
47
+ key="studio_white",
48
+ name="White Studio",
49
+ prompt="clean white photography studio background, professional lighting setup, seamless white backdrop, soft diffused light, minimal shadows",
50
+ negative_extra="colored, textured, dirty",
51
+ category="Professional",
52
+ icon="📷",
53
+ guidance_scale=8.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  ),
55
+ "coworking": SceneTemplate(
56
+ key="coworking",
57
+ name="Coworking Space",
58
+ prompt="modern coworking space, open plan office, plants, exposed brick, industrial chic design, natural light, collaborative environment",
59
+ negative_extra="empty, dark, boring",
60
+ category="Professional",
61
+ icon="💼",
62
+ guidance_scale=7.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  ),
64
+ "conference": SceneTemplate(
65
+ key="conference",
66
+ name="Conference Room",
67
+ prompt="modern conference room, large meeting table, glass walls, professional presentation screen, bright corporate lighting, clean minimal design",
68
+ negative_extra="small, cramped, outdated",
69
+ category="Professional",
70
+ icon="🤝",
71
+ guidance_scale=7.5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  ),
73
 
74
+ # Nature Category
75
+ "beach_sunset": SceneTemplate(
76
+ key="beach_sunset",
77
+ name="Sunset Beach",
78
+ prompt="beautiful tropical beach at golden hour sunset, palm trees silhouette, calm turquoise ocean waves, warm orange and pink sky, soft sand, paradise vacation vibes",
79
+ negative_extra="storm, rain, crowded, trash",
80
+ category="Nature",
81
+ icon="🏖️",
82
+ guidance_scale=7.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  ),
84
+ "forest_enchanted": SceneTemplate(
85
+ key="forest_enchanted",
86
+ name="Enchanted Forest",
87
+ prompt="magical enchanted forest, sunlight streaming through tall trees, lush green foliage, mystical atmosphere, morning mist, fairy tale woodland",
88
+ negative_extra="dead trees, dark, scary, barren",
89
+ category="Nature",
90
+ icon="🌲",
91
+ guidance_scale=7.0
92
+ ),
93
+ "mountain_scenic": SceneTemplate(
94
+ key="mountain_scenic",
95
+ name="Mountain Vista",
96
+ prompt="breathtaking mountain landscape, snow-capped peaks, alpine meadow, clear blue sky, majestic scenic view, pristine nature, peaceful atmosphere",
97
+ negative_extra="industrial, polluted, crowded",
98
+ category="Nature",
99
+ icon="🏔️",
100
+ guidance_scale=7.5
101
+ ),
102
+ "garden_spring": SceneTemplate(
103
+ key="garden_spring",
104
+ name="Spring Garden",
105
+ prompt="beautiful spring flower garden, colorful blooming flowers, roses and tulips, manicured hedges, sunny day, botanical paradise, fresh and vibrant",
106
+ negative_extra="dead, winter, wilted, dry",
107
+ category="Nature",
108
+ icon="🌸",
109
+ guidance_scale=7.0
110
+ ),
111
+ "lake_serene": SceneTemplate(
112
+ key="lake_serene",
113
+ name="Serene Lake",
114
+ prompt="peaceful serene lake at dawn, mirror-like water reflection, surrounding mountains, soft morning light, tranquil atmosphere, pristine natural beauty",
115
+ negative_extra="stormy, polluted, industrial",
116
+ category="Nature",
117
+ icon="🏞️",
118
+ guidance_scale=7.0
119
+ ),
120
+ "cherry_blossom": SceneTemplate(
121
+ key="cherry_blossom",
122
+ name="Cherry Blossom",
123
+ prompt="stunning cherry blossom trees in full bloom, pink sakura petals falling gently, Japanese garden aesthetic, soft spring sunlight, romantic atmosphere",
124
+ negative_extra="winter, dead, brown, wilted",
125
+ category="Nature",
126
+ icon="🌸",
127
+ guidance_scale=7.0
128
  ),
129
 
130
+ # Urban Category
131
+ "city_skyline": SceneTemplate(
132
+ key="city_skyline",
133
+ name="City Skyline",
134
+ prompt="modern city skyline at blue hour, impressive skyscrapers, glass buildings reflecting sunset, urban metropolitan view, cinematic atmosphere",
135
+ negative_extra="slums, dirty, abandoned, ruins",
136
+ category="Urban",
137
+ icon="🌆",
138
+ guidance_scale=7.5
139
+ ),
140
+ "cafe_cozy": SceneTemplate(
141
+ key="cafe_cozy",
142
+ name="Cozy Cafe",
143
+ prompt="warm cozy coffee shop interior, wooden furniture, ambient lighting, exposed brick walls, plants, comfortable atmosphere, artisan cafe vibes",
144
+ negative_extra="fast food, plastic, harsh lighting",
145
+ category="Urban",
146
+ icon="",
147
+ guidance_scale=7.0
148
+ ),
149
+ "street_european": SceneTemplate(
150
+ key="street_european",
151
+ name="European Street",
152
+ prompt="charming European cobblestone street, historic buildings, outdoor cafe, flowers on balconies, warm afternoon light, romantic Paris or Rome vibes",
153
+ negative_extra="modern, industrial, ugly, dirty",
154
+ category="Urban",
155
+ icon="🏛️",
156
+ guidance_scale=7.0
157
+ ),
158
+ "night_neon": SceneTemplate(
159
+ key="night_neon",
160
+ name="Neon Nightlife",
161
+ prompt="vibrant city nightlife scene, neon lights and signs, urban night atmosphere, colorful reflections on wet street, cyberpunk aesthetic, electric energy",
162
+ negative_extra="daytime, boring, plain",
163
+ category="Urban",
164
+ icon="🌃",
165
+ guidance_scale=6.5
166
+ ),
167
+ "rooftop_view": SceneTemplate(
168
+ key="rooftop_view",
169
+ name="Rooftop Terrace",
170
+ prompt="luxury rooftop terrace, city panoramic view, modern outdoor furniture, string lights, sunset golden hour, sophisticated urban oasis",
171
+ negative_extra="cheap, dirty, crowded",
172
+ category="Urban",
173
+ icon="🏙️",
174
+ guidance_scale=7.5
175
  ),
176
 
177
+ # Artistic Category
178
+ "gradient_soft": SceneTemplate(
179
+ key="gradient_soft",
180
+ name="Soft Gradient",
181
+ prompt="smooth soft gradient background, pastel colors blending beautifully, pink to blue to purple transition, dreamy aesthetic, professional portrait backdrop",
182
+ negative_extra="harsh, noisy, textured, busy",
183
+ category="Artistic",
184
  icon="🎨",
185
+ guidance_scale=8.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
  ),
187
+ "abstract_modern": SceneTemplate(
188
+ key="abstract_modern",
189
+ name="Modern Abstract",
190
+ prompt="modern abstract art background, geometric shapes, bold colors, contemporary design, artistic composition, museum gallery aesthetic",
191
+ negative_extra="realistic, plain, boring",
192
+ category="Artistic",
193
+ icon="🖼️",
194
+ guidance_scale=6.5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
  ),
196
+ "vintage_retro": SceneTemplate(
197
+ key="vintage_retro",
198
+ name="Vintage Retro",
199
+ prompt="vintage retro aesthetic background, warm sepia tones, nostalgic 70s vibes, film grain texture, classic photography style, timeless elegance",
200
+ negative_extra="modern, digital, cold, harsh",
201
+ category="Artistic",
202
+ icon="📻",
203
+ guidance_scale=7.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
  ),
205
+ "watercolor_dream": SceneTemplate(
206
+ key="watercolor_dream",
207
+ name="Watercolor Dream",
208
+ prompt="beautiful watercolor painting background, soft flowing colors, artistic brush strokes, dreamy ethereal atmosphere, delicate artistic aesthetic",
209
+ negative_extra="digital, sharp, photorealistic",
210
+ category="Artistic",
211
+ icon="🖌️",
212
+ guidance_scale=6.5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  ),
214
 
215
+ # Seasonal Category
216
+ "autumn_foliage": SceneTemplate(
217
+ key="autumn_foliage",
218
+ name="Autumn Foliage",
219
+ prompt="beautiful autumn scenery, vibrant fall foliage, orange red and golden leaves, maple trees, warm sunlight filtering through, cozy seasonal atmosphere",
220
+ negative_extra="spring, summer, green, snow",
221
+ category="Seasonal",
222
+ icon="🍂",
223
+ guidance_scale=7.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
  ),
225
+ "winter_snow": SceneTemplate(
226
+ key="winter_snow",
227
+ name="Winter Wonderland",
228
+ prompt="magical winter wonderland, fresh white snow covering everything, snow-laden pine trees, soft snowfall, peaceful cold atmosphere, holiday season vibes",
229
+ negative_extra="summer, green, rain, mud",
230
+ category="Seasonal",
231
+ icon="❄️",
232
+ guidance_scale=7.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
233
  ),
234
+ "summer_tropical": SceneTemplate(
235
+ key="summer_tropical",
236
+ name="Tropical Summer",
237
+ prompt="vibrant tropical summer scene, lush palm trees, bright sunny day, exotic flowers, paradise vacation destination, warm and inviting atmosphere",
238
+ negative_extra="winter, cold, snow, gray",
239
+ category="Seasonal",
240
+ icon="🌴",
241
+ guidance_scale=7.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
  ),
243
+ "spring_meadow": SceneTemplate(
244
+ key="spring_meadow",
245
+ name="Spring Meadow",
246
+ prompt="beautiful spring meadow, wildflowers blooming, fresh green grass, butterflies, soft warm sunlight, renewal and new beginnings, pastoral beauty",
247
+ negative_extra="winter, autumn, dead, dry",
248
+ category="Seasonal",
249
+ icon="🌷",
250
+ guidance_scale=7.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251
  ),
252
  }
253
 
254
  # Category display order
255
+ CATEGORIES = ["Professional", "Nature", "Urban", "Artistic", "Seasonal"]
256
 
257
  def __init__(self):
258
+ """Initialize the scene template manager"""
259
+ logger.info(f"SceneTemplateManager initialized with {len(self.TEMPLATES)} templates")
 
 
 
 
260
 
261
+ def get_all_templates(self) -> Dict[str, SceneTemplate]:
262
+ """Get all available templates"""
 
 
 
263
  return self.TEMPLATES
264
 
265
+ def get_template(self, key: str) -> Optional[SceneTemplate]:
266
+ """Get a specific template by key"""
 
 
 
 
 
 
 
 
 
 
 
 
267
  return self.TEMPLATES.get(key)
268
 
269
+ def get_templates_by_category(self, category: str) -> List[SceneTemplate]:
270
+ """Get all templates in a specific category"""
 
 
 
 
 
 
 
 
 
 
 
 
271
  return [t for t in self.TEMPLATES.values() if t.category == category]
272
 
273
  def get_categories(self) -> List[str]:
274
+ """Get list of all categories in display order"""
 
 
 
 
 
 
 
275
  return self.CATEGORIES
276
 
277
  def get_template_choices_sorted(self) -> List[str]:
278
  """
279
  Get template choices formatted for Gradio dropdown.
280
+ Returns list of display strings sorted A-Z: "🏢 Modern Office"
 
 
 
 
 
 
 
281
  """
282
  display_list = []
283
+ for key, template in self.TEMPLATES.items():
284
+ display_name = f"{template.icon} {template.name}"
285
+ display_list.append(display_name)
286
 
287
+ # Sort alphabetically by name (ignoring emoji)
288
+ display_list.sort(key=lambda x: x.split(' ', 1)[1] if ' ' in x else x)
 
 
 
 
289
  return display_list
290
 
291
  def get_template_key_from_display(self, display_name: str) -> Optional[str]:
292
  """
293
  Get template key from display name.
294
+ Example: "🏢 Modern Office" -> "office_modern"
 
 
 
 
 
 
 
 
 
295
  """
296
  if not display_name:
297
  return None
 
301
  return key
302
  return None
303
 
304
+ def get_prompt_for_template(self, key: str) -> Optional[str]:
305
+ """Get the prompt string for a template"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
306
  template = self.get_template(key)
307
+ return template.prompt if template else None
308
+
309
+ def get_negative_prompt_for_template(
310
+ self,
311
+ key: str,
312
+ base_negative: str = "blurry, low quality, distorted, people, characters"
313
+ ) -> str:
314
+ """Get combined negative prompt for a template"""
 
 
 
 
 
 
 
 
 
 
 
315
  template = self.get_template(key)
316
+ if template and template.negative_extra:
317
+ return f"{base_negative}, {template.negative_extra}"
318
+ return base_negative
319
 
320
+ def get_guidance_scale_for_template(self, key: str) -> float:
321
+ """Get the recommended guidance scale for a template"""
 
 
 
 
 
 
 
 
 
 
 
 
322
  template = self.get_template(key)
323
+ return template.guidance_scale if template else 7.5
 
 
324
 
325
  def build_gallery_html(self) -> str:
326
  """
327
+ Build HTML for the scene template gallery.
328
+ Returns HTML string for display in Gradio.
 
 
 
 
329
  """
330
+ html_parts = ['<div class="scene-gallery">']
331
 
332
  for category in self.CATEGORIES:
333
  templates = self.get_templates_by_category(category)
 
335
  continue
336
 
337
  html_parts.append(f'''
338
+ <div class="scene-category">
339
+ <h4 class="scene-category-title">{category}</h4>
340
+ <div class="scene-grid">
341
  ''')
342
 
343
+ for template in templates:
344
  html_parts.append(f'''
345
+ <button class="scene-card" data-template="{template.key}" onclick="selectTemplate('{template.key}')">
346
+ <span class="scene-icon">{template.icon}</span>
347
+ <span class="scene-name">{template.name}</span>
348
+ </button>
 
349
  ''')
350
 
351
  html_parts.append('</div></div>')
352
 
353
  html_parts.append('</div>')
354
+ return ''.join(html_parts)
355
+
356
+ def get_gallery_css(self) -> str:
357
+ """Get CSS styles for the scene gallery"""
358
+ return """
359
+ /* Scene Gallery Styles */
360
+ .scene-gallery {
361
+ margin: 16px 0;
362
+ }
363
+
364
+ .scene-category {
365
+ margin-bottom: 20px;
366
+ }
367
+
368
+ .scene-category-title {
369
+ font-size: 0.9rem;
370
+ font-weight: 600;
371
+ color: #475569;
372
+ margin-bottom: 12px;
373
+ padding-bottom: 8px;
374
+ border-bottom: 1px solid #e2e8f0;
375
+ }
376
+
377
+ .scene-grid {
378
+ display: grid;
379
+ grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
380
+ gap: 8px;
381
+ }
382
+
383
+ .scene-card {
384
+ display: flex;
385
+ flex-direction: column;
386
+ align-items: center;
387
+ justify-content: center;
388
+ padding: 12px 8px;
389
+ background: #f8fafc;
390
+ border: 1px solid #e2e8f0;
391
+ border-radius: 8px;
392
+ cursor: pointer;
393
+ transition: all 0.2s ease;
394
+ min-height: 70px;
395
+ }
396
+
397
+ .scene-card:hover {
398
+ background: #dbeafe;
399
+ border-color: #3b82f6;
400
+ transform: translateY(-2px);
401
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
402
+ }
403
+
404
+ .scene-card.selected {
405
+ background: #dbeafe;
406
+ border-color: #3b82f6;
407
+ box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.3);
408
+ }
409
+
410
+ .scene-icon {
411
+ font-size: 1.5rem;
412
+ margin-bottom: 4px;
413
+ }
414
+
415
+ .scene-name {
416
+ font-size: 0.75rem;
417
+ font-weight: 500;
418
+ color: #1e293b;
419
+ text-align: center;
420
+ line-height: 1.2;
421
+ }
422
+
423
+ @media (max-width: 768px) {
424
+ .scene-grid {
425
+ grid-template-columns: repeat(3, 1fr);
426
+ }
427
+ }
428
+ """