Spaces:
Running
on
Zero
Running
on
Zero
Add demo presets gallery to UI
Browse files- Collapsible accordion showing 3 example character sheets
- Gallery view with thumbnails for Character, Demo2, Demo3
- Shows input type (Face Only / Full Body) for each demo
- Demo loading functions for future Scene Composer integration
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
app.py
CHANGED
|
@@ -179,6 +179,69 @@ def load_character_preset(preset_id: str) -> Tuple[Optional[Image.Image], str, s
|
|
| 179 |
return None, "", "Auto/Neutral"
|
| 180 |
|
| 181 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
# =============================================================================
|
| 183 |
# Character Sheet Metadata
|
| 184 |
# =============================================================================
|
|
@@ -805,6 +868,36 @@ APP_CSS = """
|
|
| 805 |
.generate-btn-main:hover {
|
| 806 |
background: linear-gradient(90deg, #00cc55, #00ee66) !important;
|
| 807 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 808 |
"""
|
| 809 |
|
| 810 |
|
|
@@ -823,6 +916,34 @@ def create_ui():
|
|
| 823 |
gr.Markdown("# Character Sheet Pro")
|
| 824 |
gr.Markdown("Generate 7-view character turnaround sheets and compose scenes with your characters.")
|
| 825 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 826 |
# Shared controls (outside tabs)
|
| 827 |
with gr.Row():
|
| 828 |
backend_dropdown = gr.Dropdown(
|
|
|
|
| 179 |
return None, "", "Auto/Neutral"
|
| 180 |
|
| 181 |
|
| 182 |
+
# =============================================================================
|
| 183 |
+
# Demo Presets Loading
|
| 184 |
+
# =============================================================================
|
| 185 |
+
|
| 186 |
+
DEMOS_DIR = Path("./demos")
|
| 187 |
+
|
| 188 |
+
# Demo configuration
|
| 189 |
+
DEMO_PRESETS = [
|
| 190 |
+
{
|
| 191 |
+
"id": "demo1",
|
| 192 |
+
"name": "Character",
|
| 193 |
+
"folder": "demo1",
|
| 194 |
+
"input_type": "Full Body",
|
| 195 |
+
"description": "Full body character with detailed outfit"
|
| 196 |
+
},
|
| 197 |
+
{
|
| 198 |
+
"id": "demo2",
|
| 199 |
+
"name": "Demo2",
|
| 200 |
+
"folder": "demo2",
|
| 201 |
+
"input_type": "Full Body",
|
| 202 |
+
"description": "Full body character example"
|
| 203 |
+
},
|
| 204 |
+
{
|
| 205 |
+
"id": "demo3",
|
| 206 |
+
"name": "Demo3",
|
| 207 |
+
"folder": "demo3",
|
| 208 |
+
"input_type": "Face Only",
|
| 209 |
+
"description": "Face-only input with generated body"
|
| 210 |
+
},
|
| 211 |
+
]
|
| 212 |
+
|
| 213 |
+
|
| 214 |
+
def get_demo_thumbnail(demo_id: str) -> Optional[str]:
|
| 215 |
+
"""Get the path to a demo's character sheet thumbnail."""
|
| 216 |
+
for demo in DEMO_PRESETS:
|
| 217 |
+
if demo["id"] == demo_id:
|
| 218 |
+
folder = DEMOS_DIR / demo["folder"]
|
| 219 |
+
# Find the character sheet file
|
| 220 |
+
for f in folder.glob("*_character_sheet.png"):
|
| 221 |
+
return str(f)
|
| 222 |
+
return None
|
| 223 |
+
|
| 224 |
+
|
| 225 |
+
def get_all_demo_thumbnails() -> List[Tuple[str, str]]:
|
| 226 |
+
"""Get all demo thumbnails as (path, caption) tuples for gallery."""
|
| 227 |
+
thumbnails = []
|
| 228 |
+
for demo in DEMO_PRESETS:
|
| 229 |
+
folder = DEMOS_DIR / demo["folder"]
|
| 230 |
+
for f in folder.glob("*_character_sheet.png"):
|
| 231 |
+
caption = f"{demo['name']} ({demo['input_type']})"
|
| 232 |
+
thumbnails.append((str(f), caption))
|
| 233 |
+
break
|
| 234 |
+
return thumbnails
|
| 235 |
+
|
| 236 |
+
|
| 237 |
+
def load_demo_for_scene_composer(demo_id: str) -> Optional[Image.Image]:
|
| 238 |
+
"""Load a demo character sheet for use in Scene Composer."""
|
| 239 |
+
thumb_path = get_demo_thumbnail(demo_id)
|
| 240 |
+
if thumb_path and Path(thumb_path).exists():
|
| 241 |
+
return Image.open(thumb_path)
|
| 242 |
+
return None
|
| 243 |
+
|
| 244 |
+
|
| 245 |
# =============================================================================
|
| 246 |
# Character Sheet Metadata
|
| 247 |
# =============================================================================
|
|
|
|
| 868 |
.generate-btn-main:hover {
|
| 869 |
background: linear-gradient(90deg, #00cc55, #00ee66) !important;
|
| 870 |
}
|
| 871 |
+
|
| 872 |
+
/* Demo presets gallery */
|
| 873 |
+
.demo-gallery {
|
| 874 |
+
margin: 16px 0;
|
| 875 |
+
}
|
| 876 |
+
|
| 877 |
+
.demo-gallery .gallery-item {
|
| 878 |
+
border-radius: 8px;
|
| 879 |
+
overflow: hidden;
|
| 880 |
+
transition: transform 0.2s, box-shadow 0.2s;
|
| 881 |
+
}
|
| 882 |
+
|
| 883 |
+
.demo-gallery .gallery-item:hover {
|
| 884 |
+
transform: scale(1.02);
|
| 885 |
+
box-shadow: 0 4px 20px rgba(168, 85, 247, 0.4);
|
| 886 |
+
}
|
| 887 |
+
|
| 888 |
+
.demo-section {
|
| 889 |
+
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
| 890 |
+
border-radius: 12px;
|
| 891 |
+
padding: 16px;
|
| 892 |
+
margin-bottom: 20px;
|
| 893 |
+
border: 1px solid #7c3aed;
|
| 894 |
+
}
|
| 895 |
+
|
| 896 |
+
.demo-label {
|
| 897 |
+
color: #a855f7;
|
| 898 |
+
font-weight: bold;
|
| 899 |
+
margin-bottom: 8px;
|
| 900 |
+
}
|
| 901 |
"""
|
| 902 |
|
| 903 |
|
|
|
|
| 916 |
gr.Markdown("# Character Sheet Pro")
|
| 917 |
gr.Markdown("Generate 7-view character turnaround sheets and compose scenes with your characters.")
|
| 918 |
|
| 919 |
+
# Demo Presets Section
|
| 920 |
+
with gr.Accordion("Example Outputs (Click to expand)", open=False, elem_classes=["demo-section"]):
|
| 921 |
+
gr.Markdown("### Demo Character Sheets")
|
| 922 |
+
gr.Markdown("These examples show what Character Sheet Pro can generate. Click on an image to view it full size.")
|
| 923 |
+
|
| 924 |
+
# Load demo thumbnails
|
| 925 |
+
demo_thumbnails = get_all_demo_thumbnails()
|
| 926 |
+
|
| 927 |
+
if demo_thumbnails:
|
| 928 |
+
demo_gallery = gr.Gallery(
|
| 929 |
+
value=demo_thumbnails,
|
| 930 |
+
label="Example Outputs",
|
| 931 |
+
show_label=False,
|
| 932 |
+
columns=3,
|
| 933 |
+
rows=1,
|
| 934 |
+
height=300,
|
| 935 |
+
object_fit="contain",
|
| 936 |
+
elem_classes=["demo-gallery"]
|
| 937 |
+
)
|
| 938 |
+
|
| 939 |
+
with gr.Row():
|
| 940 |
+
for d in DEMO_PRESETS:
|
| 941 |
+
with gr.Column(scale=1, min_width=150):
|
| 942 |
+
gr.Markdown(f"**{d['name']}**")
|
| 943 |
+
gr.Markdown(f"Input: {d['input_type']}")
|
| 944 |
+
else:
|
| 945 |
+
gr.Markdown("*Demo images not available*")
|
| 946 |
+
|
| 947 |
# Shared controls (outside tabs)
|
| 948 |
with gr.Row():
|
| 949 |
backend_dropdown = gr.Dropdown(
|