File size: 5,968 Bytes
467c85e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
de132df
467c85e
de132df
 
 
 
467c85e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
de132df
 
 
 
467c85e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
de132df
467c85e
de132df
 
 
467c85e
 
de132df
467c85e
 
 
 
 
 
de132df
 
 
467c85e
de132df
467c85e
 
 
 
 
 
 
de132df
467c85e
 
 
 
 
 
 
de132df
 
 
467c85e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
de132df
467c85e
 
 
 
 
 
 
 
 
 
de132df
467c85e
 
 
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
import yaml
import random
import os
import json
import tempfile
import gradio as gr
from .config import FEATURES_FILE, FEATURE_SEQUENCE, SECTIONS, EXAMPLES_DIR

def load_features():
    with open(FEATURES_FILE, "r") as f:
        return yaml.safe_load(f)

features_data = load_features()

def get_detail(category, subcategory, key):
    """Retrieves the detailed description for a given key in a category/subcategory."""
    return features_data.get(category, {}).get(subcategory, {}).get(key, key)

def generate_prompt(*args):
    """
    Assembles the prompt based on dropdown selections and extra text info.
    """
    character_name = args[0]
    num_features = len(FEATURE_SEQUENCE)
    feature_keys = args[1:num_features+1]
    # Checkboxes are at args[num_features+1 : num_features+1 + num_features]
    # Extra info starts after the name, dropdowns, and checkboxes
    extra_infos = args[1 + num_features*2 : 1 + num_features*2 + len(SECTIONS)]

    template = features_data.get("templates", {}).get("default", "")
    
    context = {}
    for i, (cat, subcat, t_key) in enumerate(FEATURE_SEQUENCE):
        key = feature_keys[i]
        context[t_key] = get_detail(cat, subcat, key)
    
    # Handle multiple accessories dynamically
    acc_list = []
    # Identify accessory keys in context
    for i, (cat, subcat, t_key) in enumerate(FEATURE_SEQUENCE):
        if subcat == 'accessory' and feature_keys[i] != "None":
            acc_list.append(context[t_key])
    
    if acc_list:
        context['accessories'] = ", and has " + " as well as ".join(acc_list)
    else:
        context['accessories'] = ""
    
    # Inject extra info into the context
    if extra_infos[0]: # Identity
        context['age'] += f", {extra_infos[0]}"
    if extra_infos[1]: # Appearance
        context['distinguishing_feature'] += f", also {extra_infos[1]}"
    if extra_infos[2]: # Equipment
        if context['accessories']:
            context['accessories'] += f", further complemented by {extra_infos[2]}"
        else:
            context['accessories'] = f", complemented by {extra_infos[2]}"
    if extra_infos[3]: # Environment
        context['atmosphere'] += f", additionally {extra_infos[3]}"
    if extra_infos[4]: # Style
        context['camera'] += f", art style notes: {extra_infos[4]}"
    
    try:
        base_prompt = template.format(**context)
        if character_name and character_name.strip() and character_name != "Unnamed Hero":
            return f"A portrait of {character_name}, {base_prompt}"
        return base_prompt
    except Exception as e:
        return f"Error building prompt: {e}"

def handle_regeneration(*args):
    """Randomizes checkboxes and returns new values for dropdowns."""
    num_features = len(FEATURE_SEQUENCE)
    current_values = list(args[:num_features])
    checkboxes = args[num_features : num_features*2]
    
    new_values = []
    for i, (is_random, (cat, subcat, t_key)) in enumerate(zip(checkboxes, FEATURE_SEQUENCE)):
        if is_random:
            choices = list(features_data[cat][subcat].keys())
            new_values.append(random.choice(choices))
        else:
            new_values.append(current_values[i])
            
    return new_values

def save_character(*args):
    """Saves all current UI values to a JSON file."""
    character_name = args[0]
    num_features = len(FEATURE_SEQUENCE)
    feature_keys = args[1:num_features+1]
    checkboxes = args[num_features+1 : num_features+1 + num_features]
    extra_infos = args[num_features+1 + num_features : num_features+1 + num_features + len(SECTIONS)]
    
    data = {
        "name": character_name,
        "features": {FEATURE_SEQUENCE[i][2]: feature_keys[i] for i in range(num_features)},
        "randomization": {FEATURE_SEQUENCE[i][2]: checkboxes[i] for i in range(num_features)},
        "extra_info": {SECTIONS[i].lower(): extra_infos[i] for i in range(len(SECTIONS))}
    }
    
    # Save to a file with a friendly name in a temp directory
    safe_name = "".join([c if c.isalnum() else "_" for c in character_name]).strip("_")
    filename = f"{safe_name}_data.json" if safe_name else "rpg_character_data.json"
    
    temp_dir = tempfile.mkdtemp()
    path = os.path.join(temp_dir, filename)
    with open(path, 'w') as f:
        json.dump(data, f, indent=4)
    return path

def load_character(file):
    """Loads UI values from a JSON file."""
    if file is None:
        return [gr.update()] * (1 + len(FEATURE_SEQUENCE) * 2 + len(SECTIONS))
    
    try:
        file_path = file.name if hasattr(file, 'name') else file
        with open(file_path, 'r') as f:
            data = json.load(f)
        
        updates = []
        # Update name
        updates.append(data.get("name", "Unnamed Hero"))
        
        # Update dropdowns
        for _, _, t_key in FEATURE_SEQUENCE:
            updates.append(data.get("features", {}).get(t_key, gr.update()))
        
        # Update checkboxes (randomization flags)
        for _, _, t_key in FEATURE_SEQUENCE:
            updates.append(data.get("randomization", {}).get(t_key, gr.update()))
        
        # Update extra info textboxes
        for section in SECTIONS:
            updates.append(data.get("extra_info", {}).get(section.lower(), ""))
            
        return updates
    except Exception as e:
        print(f"Error loading character: {e}")
        return [gr.update()] * (1 + len(FEATURE_SEQUENCE) * 2 + len(SECTIONS))

def get_example_list():
    """Returns a list of JSON filenames in the examples directory."""
    if not os.path.exists(EXAMPLES_DIR):
        return []
    return [f for f in os.listdir(EXAMPLES_DIR) if f.endswith('.json')]

def load_example_character(filename):
    """Loads a character config from the examples directory."""
    if not filename:
        return [gr.update()] * (1 + len(FEATURE_SEQUENCE) * 2 + len(SECTIONS))
    
    path = os.path.join(EXAMPLES_DIR, filename)
    return load_character(path)