gradio-themer 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gradio_themer/__init__.py +3 -0
- gradio_themer/generate_theme_template.py +169 -0
- gradio_themer/gradio_themer.py +336 -0
- gradio_themer/gradio_themer.pyi +490 -0
- gradio_themer/templates/component/index.js +1256 -0
- gradio_themer/templates/component/style.css +1 -0
- gradio_themer/templates/example/index.js +103 -0
- gradio_themer/templates/example/style.css +1 -0
- gradio_themer-0.1.0.dist-info/METADATA +405 -0
- gradio_themer-0.1.0.dist-info/RECORD +11 -0
- gradio_themer-0.1.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,169 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Generate theme template for Gradio Themer component
|
4
|
+
Usage: python generate_theme_template.py --name "My Theme" --background "#ffffff"
|
5
|
+
"""
|
6
|
+
|
7
|
+
import json
|
8
|
+
import argparse
|
9
|
+
from pathlib import Path
|
10
|
+
from typing import Dict, Any
|
11
|
+
|
12
|
+
|
13
|
+
def generate_theme_template(
|
14
|
+
theme_name: str, background_color: str = "#ffffff"
|
15
|
+
) -> Dict[str, Any]:
|
16
|
+
"""
|
17
|
+
Generate a theme template with sensible defaults
|
18
|
+
|
19
|
+
Args:
|
20
|
+
theme_name: Display name for the theme
|
21
|
+
background_color: Main background color for the theme
|
22
|
+
|
23
|
+
Returns:
|
24
|
+
Dictionary containing the complete theme configuration
|
25
|
+
"""
|
26
|
+
theme_key = theme_name.lower().replace(" ", "_").replace("-", "_")
|
27
|
+
|
28
|
+
template = {
|
29
|
+
"themes": {
|
30
|
+
theme_key: {
|
31
|
+
"name": theme_name,
|
32
|
+
"colors": {
|
33
|
+
# Base colors - main backgrounds and text
|
34
|
+
"base-100": "#ffffff", # Main background
|
35
|
+
"base-200": "#f8fafc", # Secondary background
|
36
|
+
"base-300": "#e2e8f0", # Border color
|
37
|
+
"base-content": "#1e293b", # Main text color
|
38
|
+
# Primary colors - main action buttons
|
39
|
+
"primary": "#3b82f6", # Primary button color
|
40
|
+
"primary-content": "#ffffff", # Primary button text
|
41
|
+
# Secondary colors - secondary actions
|
42
|
+
"secondary": "#64748b", # Secondary button color
|
43
|
+
"secondary-content": "#ffffff", # Secondary button text
|
44
|
+
# Accent colors - highlights and special elements
|
45
|
+
"accent": "#f59e0b", # Accent color
|
46
|
+
"accent-content": "#000000", # Accent text color
|
47
|
+
# Neutral colors - neutral elements
|
48
|
+
"neutral": "#374151", # Neutral color
|
49
|
+
"neutral-content": "#ffffff", # Neutral text color
|
50
|
+
# Status colors
|
51
|
+
"error": "#ef4444", # Error color
|
52
|
+
"error-content": "#ffffff", # Error text color
|
53
|
+
},
|
54
|
+
"background": background_color, # Overall theme background
|
55
|
+
}
|
56
|
+
},
|
57
|
+
"default_theme": theme_key,
|
58
|
+
"default_font": "Inter",
|
59
|
+
}
|
60
|
+
|
61
|
+
return template
|
62
|
+
|
63
|
+
|
64
|
+
def add_theme_to_existing_config(
|
65
|
+
config_path: str, theme_name: str, background_color: str = "#ffffff"
|
66
|
+
) -> bool:
|
67
|
+
"""
|
68
|
+
Add a new theme to an existing configuration file
|
69
|
+
|
70
|
+
Args:
|
71
|
+
config_path: Path to existing configuration file
|
72
|
+
theme_name: Name of the new theme
|
73
|
+
background_color: Background color for the theme
|
74
|
+
|
75
|
+
Returns:
|
76
|
+
True if successful, False otherwise
|
77
|
+
"""
|
78
|
+
try:
|
79
|
+
# Load existing config
|
80
|
+
with open(config_path, "r") as f:
|
81
|
+
config = json.load(f)
|
82
|
+
|
83
|
+
# Generate new theme
|
84
|
+
new_template = generate_theme_template(theme_name, background_color)
|
85
|
+
theme_key = list(new_template["themes"].keys())[0]
|
86
|
+
|
87
|
+
# Add to existing themes
|
88
|
+
if "themes" not in config:
|
89
|
+
config["themes"] = {}
|
90
|
+
|
91
|
+
config["themes"][theme_key] = new_template["themes"][theme_key]
|
92
|
+
|
93
|
+
# Write back to file
|
94
|
+
with open(config_path, "w") as f:
|
95
|
+
json.dump(config, f, indent=2)
|
96
|
+
|
97
|
+
return True
|
98
|
+
|
99
|
+
except Exception as e:
|
100
|
+
print(f"❌ Error adding theme to existing config: {e}")
|
101
|
+
return False
|
102
|
+
|
103
|
+
|
104
|
+
def main():
|
105
|
+
parser = argparse.ArgumentParser(
|
106
|
+
description="Generate Gradio theme template",
|
107
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
108
|
+
epilog="""
|
109
|
+
Examples:
|
110
|
+
# Generate new theme config file
|
111
|
+
python generate_theme_template.py --name "My Theme"
|
112
|
+
|
113
|
+
# Generate with custom background
|
114
|
+
python generate_theme_template.py --name "Dark Theme" --background "#1f2937"
|
115
|
+
|
116
|
+
# Add to existing config file
|
117
|
+
python generate_theme_template.py --name "New Theme" --add-to existing_themes.json
|
118
|
+
|
119
|
+
# Generate with custom output path
|
120
|
+
python generate_theme_template.py --name "Corporate" --output corporate_themes.json
|
121
|
+
""",
|
122
|
+
)
|
123
|
+
|
124
|
+
parser.add_argument("--name", required=True, help="Theme display name")
|
125
|
+
parser.add_argument(
|
126
|
+
"--background", default="#ffffff", help="Background color (default: #ffffff)"
|
127
|
+
)
|
128
|
+
parser.add_argument(
|
129
|
+
"--output",
|
130
|
+
default="user_themes.json",
|
131
|
+
help="Output file path (default: user_themes.json)",
|
132
|
+
)
|
133
|
+
parser.add_argument(
|
134
|
+
"--add-to",
|
135
|
+
dest="add_to",
|
136
|
+
help="Add theme to existing config file instead of creating new one",
|
137
|
+
)
|
138
|
+
|
139
|
+
args = parser.parse_args()
|
140
|
+
|
141
|
+
if args.add_to:
|
142
|
+
# Add to existing configuration
|
143
|
+
if not Path(args.add_to).exists():
|
144
|
+
print(f"❌ Configuration file {args.add_to} does not exist")
|
145
|
+
return 1
|
146
|
+
|
147
|
+
if add_theme_to_existing_config(args.add_to, args.name, args.background):
|
148
|
+
print(f"✅ Added theme '{args.name}' to {args.add_to}")
|
149
|
+
print(f"📝 Edit the file to customize colors further")
|
150
|
+
else:
|
151
|
+
print(f"❌ Failed to add theme to {args.add_to}")
|
152
|
+
return 1
|
153
|
+
else:
|
154
|
+
# Generate new configuration file
|
155
|
+
template = generate_theme_template(args.name, args.background)
|
156
|
+
|
157
|
+
# Write to file
|
158
|
+
with open(args.output, "w") as f:
|
159
|
+
json.dump(template, f, indent=2)
|
160
|
+
|
161
|
+
print(f"✅ Generated theme template: {args.output}")
|
162
|
+
print(f"📝 Edit the file to customize colors and add more themes")
|
163
|
+
print(f"🎨 Theme key: {list(template['themes'].keys())[0]}")
|
164
|
+
|
165
|
+
return 0
|
166
|
+
|
167
|
+
|
168
|
+
if __name__ == "__main__":
|
169
|
+
exit(main())
|
@@ -0,0 +1,336 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import json
|
4
|
+
import os
|
5
|
+
from pathlib import Path
|
6
|
+
from typing import Any, Dict, Callable, Sequence, Optional
|
7
|
+
from gradio.components.base import FormComponent
|
8
|
+
from gradio.events import Events
|
9
|
+
|
10
|
+
|
11
|
+
class GradioThemer(FormComponent):
|
12
|
+
"""
|
13
|
+
A custom Gradio component for applying user-configurable themes to Gradio applications.
|
14
|
+
|
15
|
+
This component allows users to:
|
16
|
+
- Configure unlimited custom themes via JSON configuration files
|
17
|
+
- Preview themes with live Gradio components
|
18
|
+
- Switch between themes dynamically
|
19
|
+
- Export CSS for use in other projects
|
20
|
+
"""
|
21
|
+
|
22
|
+
EVENTS = [
|
23
|
+
Events.change,
|
24
|
+
Events.input,
|
25
|
+
Events.submit,
|
26
|
+
]
|
27
|
+
|
28
|
+
def __init__(
|
29
|
+
self,
|
30
|
+
value: Dict[str, Any] | Callable | None = None,
|
31
|
+
theme_config_path: Optional[str] = None,
|
32
|
+
*,
|
33
|
+
label: str | None = None,
|
34
|
+
every: float | None = None,
|
35
|
+
inputs: (
|
36
|
+
FormComponent | Sequence[FormComponent] | set[FormComponent] | None
|
37
|
+
) = None,
|
38
|
+
show_label: bool | None = None,
|
39
|
+
scale: int | None = None,
|
40
|
+
min_width: int = 160,
|
41
|
+
interactive: bool | None = None,
|
42
|
+
visible: bool = True,
|
43
|
+
elem_id: str | None = None,
|
44
|
+
elem_classes: list[str] | str | None = None,
|
45
|
+
render: bool = True,
|
46
|
+
key: int | str | None = None,
|
47
|
+
):
|
48
|
+
"""
|
49
|
+
Parameters:
|
50
|
+
value: Default theme configuration. Should be a dict with 'themeInput', 'themeConfig', and 'generatedCSS' keys.
|
51
|
+
theme_config_path: Path to user themes configuration file (JSON). If None, searches for common filenames.
|
52
|
+
label: The label for this component, displayed above the component if `show_label` is `True`.
|
53
|
+
every: Continously calls `value` to recalculate it if `value` is a function.
|
54
|
+
inputs: Components that are used as inputs to calculate `value` if `value` is a function.
|
55
|
+
show_label: If True, will display label.
|
56
|
+
scale: Relative size compared to adjacent Components.
|
57
|
+
min_width: Minimum pixel width.
|
58
|
+
interactive: If True, will be rendered as an editable component.
|
59
|
+
visible: If False, component will be hidden.
|
60
|
+
elem_id: An optional string that is assigned as the id of this component in the HTML DOM.
|
61
|
+
elem_classes: An optional list of strings that are assigned as the classes of this component in the HTML DOM.
|
62
|
+
render: If False, component will not render be rendered in the Blocks context.
|
63
|
+
key: A unique key for this component.
|
64
|
+
"""
|
65
|
+
# Load user themes from configuration before calling super().__init__
|
66
|
+
self.user_themes = self._load_user_themes(theme_config_path)
|
67
|
+
|
68
|
+
super().__init__(
|
69
|
+
label=label,
|
70
|
+
every=every,
|
71
|
+
inputs=inputs,
|
72
|
+
show_label=show_label,
|
73
|
+
scale=scale,
|
74
|
+
min_width=min_width,
|
75
|
+
interactive=interactive,
|
76
|
+
visible=visible,
|
77
|
+
elem_id=elem_id,
|
78
|
+
elem_classes=elem_classes,
|
79
|
+
value=value,
|
80
|
+
render=render,
|
81
|
+
key=key,
|
82
|
+
)
|
83
|
+
|
84
|
+
def _load_user_themes(self, config_path: Optional[str] = None) -> Dict[str, Any]:
|
85
|
+
"""
|
86
|
+
Load themes from user configuration file
|
87
|
+
|
88
|
+
Args:
|
89
|
+
config_path: Optional path to theme configuration file
|
90
|
+
|
91
|
+
Returns:
|
92
|
+
Dictionary containing user themes, or built-in themes as fallback
|
93
|
+
"""
|
94
|
+
# Default paths to search for theme config
|
95
|
+
search_paths = [
|
96
|
+
config_path,
|
97
|
+
"user_themes.json",
|
98
|
+
"themes.json",
|
99
|
+
"gradio_themes.json",
|
100
|
+
os.path.expanduser("~/.gradio/gradio_themes.json"),
|
101
|
+
# Also check in the component's directory for the example file
|
102
|
+
os.path.join(
|
103
|
+
os.path.dirname(__file__), "..", "..", "user_themes_example.json"
|
104
|
+
),
|
105
|
+
]
|
106
|
+
|
107
|
+
for path in search_paths:
|
108
|
+
if path and Path(path).exists():
|
109
|
+
try:
|
110
|
+
with open(path, "r") as f:
|
111
|
+
config = json.load(f)
|
112
|
+
themes = config.get("themes", {})
|
113
|
+
if themes:
|
114
|
+
print(f"✅ Loaded {len(themes)} user themes from {path}")
|
115
|
+
return themes
|
116
|
+
except Exception as e:
|
117
|
+
print(f"⚠️ Error loading theme config from {path}: {e}")
|
118
|
+
continue
|
119
|
+
|
120
|
+
# Return built-in fallback themes if no config found
|
121
|
+
print("📝 No user theme config found, using built-in themes")
|
122
|
+
return self._get_builtin_themes()
|
123
|
+
|
124
|
+
def _get_builtin_themes(self) -> Dict[str, Any]:
|
125
|
+
"""Get the built-in fallback themes"""
|
126
|
+
return {
|
127
|
+
"corporate": {
|
128
|
+
"name": "Corporate",
|
129
|
+
"colors": {
|
130
|
+
"base-100": "oklch(100% 0 0)",
|
131
|
+
"base-200": "oklch(96% 0.02 276.935)",
|
132
|
+
"base-300": "oklch(90% 0.05 293.541)",
|
133
|
+
"base-content": "oklch(22.389% 0.031 278.072)",
|
134
|
+
"primary": "oklch(58% 0.158 241.966)",
|
135
|
+
"primary-content": "oklch(100% 0 0)",
|
136
|
+
"secondary": "oklch(55% 0.046 257.417)",
|
137
|
+
"secondary-content": "oklch(100% 0 0)",
|
138
|
+
"accent": "oklch(60% 0.118 184.704)",
|
139
|
+
"accent-content": "oklch(100% 0 0)",
|
140
|
+
"neutral": "oklch(0% 0 0)",
|
141
|
+
"neutral-content": "oklch(100% 0 0)",
|
142
|
+
"error": "oklch(70% 0.191 22.216)",
|
143
|
+
"error-content": "oklch(0% 0 0)",
|
144
|
+
},
|
145
|
+
"background": "#06b6d4",
|
146
|
+
},
|
147
|
+
"cupcake": {
|
148
|
+
"name": "Cupcake",
|
149
|
+
"colors": {
|
150
|
+
"base-100": "oklch(100% 0 0)",
|
151
|
+
"base-200": "oklch(96% 0.014 340.77)",
|
152
|
+
"base-300": "oklch(92% 0.021 340.77)",
|
153
|
+
"base-content": "oklch(22.389% 0.031 278.072)",
|
154
|
+
"primary": "oklch(65.69% 0.196 342.55)",
|
155
|
+
"primary-content": "oklch(100% 0 0)",
|
156
|
+
"secondary": "oklch(74.51% 0.167 183.61)",
|
157
|
+
"secondary-content": "oklch(100% 0 0)",
|
158
|
+
"accent": "oklch(74.51% 0.167 183.61)",
|
159
|
+
"accent-content": "oklch(100% 0 0)",
|
160
|
+
"neutral": "oklch(22.389% 0.031 278.072)",
|
161
|
+
"neutral-content": "oklch(100% 0 0)",
|
162
|
+
"error": "oklch(70% 0.191 22.216)",
|
163
|
+
"error-content": "oklch(0% 0 0)",
|
164
|
+
},
|
165
|
+
"background": "#faf0e6",
|
166
|
+
},
|
167
|
+
"dark": {
|
168
|
+
"name": "Dark",
|
169
|
+
"colors": {
|
170
|
+
"base-100": "oklch(25.3% 0.015 252.417)",
|
171
|
+
"base-200": "oklch(22.2% 0.013 252.417)",
|
172
|
+
"base-300": "oklch(19.1% 0.011 252.417)",
|
173
|
+
"base-content": "oklch(74.6% 0.019 83.916)",
|
174
|
+
"primary": "oklch(65.69% 0.196 275.75)",
|
175
|
+
"primary-content": "oklch(100% 0 0)",
|
176
|
+
"secondary": "oklch(74.51% 0.167 183.61)",
|
177
|
+
"secondary-content": "oklch(100% 0 0)",
|
178
|
+
"accent": "oklch(74.51% 0.167 183.61)",
|
179
|
+
"accent-content": "oklch(100% 0 0)",
|
180
|
+
"neutral": "oklch(25.3% 0.015 252.417)",
|
181
|
+
"neutral-content": "oklch(74.6% 0.019 83.916)",
|
182
|
+
"error": "oklch(70% 0.191 22.216)",
|
183
|
+
"error-content": "oklch(0% 0 0)",
|
184
|
+
},
|
185
|
+
"background": "#1f2937",
|
186
|
+
},
|
187
|
+
"emerald": {
|
188
|
+
"name": "Emerald",
|
189
|
+
"colors": {
|
190
|
+
"base-100": "oklch(100% 0 0)",
|
191
|
+
"base-200": "oklch(96% 0.014 154.77)",
|
192
|
+
"base-300": "oklch(92% 0.021 154.77)",
|
193
|
+
"base-content": "oklch(22.389% 0.031 278.072)",
|
194
|
+
"primary": "oklch(65.69% 0.196 162.55)",
|
195
|
+
"primary-content": "oklch(100% 0 0)",
|
196
|
+
"secondary": "oklch(74.51% 0.167 183.61)",
|
197
|
+
"secondary-content": "oklch(100% 0 0)",
|
198
|
+
"accent": "oklch(74.51% 0.167 183.61)",
|
199
|
+
"accent-content": "oklch(100% 0 0)",
|
200
|
+
"neutral": "oklch(22.389% 0.031 278.072)",
|
201
|
+
"neutral-content": "oklch(100% 0 0)",
|
202
|
+
"error": "oklch(70% 0.191 22.216)",
|
203
|
+
"error-content": "oklch(0% 0 0)",
|
204
|
+
},
|
205
|
+
"background": "#ecfdf5",
|
206
|
+
},
|
207
|
+
}
|
208
|
+
|
209
|
+
def preprocess(self, payload: Dict[str, Any] | None) -> Dict[str, Any] | None:
|
210
|
+
"""
|
211
|
+
Parameters:
|
212
|
+
payload: The theme configuration data from the frontend.
|
213
|
+
Returns:
|
214
|
+
Passes the theme configuration as a dict into the function.
|
215
|
+
"""
|
216
|
+
if payload is None:
|
217
|
+
return None
|
218
|
+
|
219
|
+
# Ensure we have the expected structure
|
220
|
+
if isinstance(payload, dict):
|
221
|
+
# Handle different input formats
|
222
|
+
result = {
|
223
|
+
"themeInput": payload.get("themeInput", ""),
|
224
|
+
"themeConfig": payload.get("themeConfig"),
|
225
|
+
"generatedCSS": payload.get("generatedCSS", ""),
|
226
|
+
}
|
227
|
+
|
228
|
+
# Include additional fields if present
|
229
|
+
if "currentTheme" in payload:
|
230
|
+
result["currentTheme"] = payload["currentTheme"]
|
231
|
+
if "type" in payload:
|
232
|
+
result["type"] = payload["type"]
|
233
|
+
|
234
|
+
return result
|
235
|
+
|
236
|
+
return None
|
237
|
+
|
238
|
+
def postprocess(self, value: Dict[str, Any] | None) -> Dict[str, Any] | None:
|
239
|
+
"""
|
240
|
+
Parameters:
|
241
|
+
value: Expects a dict with theme configuration data.
|
242
|
+
Returns:
|
243
|
+
The value to display in the component, including user themes.
|
244
|
+
"""
|
245
|
+
if value is None:
|
246
|
+
result = self._get_default_value()
|
247
|
+
elif isinstance(value, dict):
|
248
|
+
# Handle different input formats
|
249
|
+
if "currentTheme" in value:
|
250
|
+
# Handle theme selection format
|
251
|
+
theme_name = value.get("currentTheme", "light")
|
252
|
+
result = {
|
253
|
+
"currentTheme": theme_name,
|
254
|
+
"themeInput": value.get("themeInput", ""),
|
255
|
+
"themeConfig": value.get("themeConfig"),
|
256
|
+
"generatedCSS": value.get("generatedCSS", ""),
|
257
|
+
"type": value.get("type", "builtin"),
|
258
|
+
"font": value.get(
|
259
|
+
"font",
|
260
|
+
{"family": "Inter", "weights": ["400", "500", "600", "700"]},
|
261
|
+
),
|
262
|
+
"removeBorders": value.get("removeBorders", True),
|
263
|
+
}
|
264
|
+
else:
|
265
|
+
# Handle raw theme configuration format
|
266
|
+
result = {
|
267
|
+
"themeInput": value.get("themeInput", ""),
|
268
|
+
"themeConfig": value.get("themeConfig"),
|
269
|
+
"generatedCSS": value.get("generatedCSS", ""),
|
270
|
+
"font": value.get(
|
271
|
+
"font",
|
272
|
+
{"family": "Inter", "weights": ["400", "500", "600", "700"]},
|
273
|
+
),
|
274
|
+
"removeBorders": value.get("removeBorders", True),
|
275
|
+
}
|
276
|
+
else:
|
277
|
+
result = self._get_default_value()
|
278
|
+
|
279
|
+
# Inject user themes into the result for frontend consumption
|
280
|
+
result["available_themes"] = self.user_themes
|
281
|
+
|
282
|
+
return result
|
283
|
+
|
284
|
+
def _get_default_value(self) -> Dict[str, Any]:
|
285
|
+
"""Get the default theme configuration"""
|
286
|
+
emerald_theme = """@theme "emerald" {
|
287
|
+
name: "emerald";
|
288
|
+
default: true;
|
289
|
+
prefersdark: false;
|
290
|
+
color-scheme: "light";
|
291
|
+
--color-base-100: oklch(100% 0 0);
|
292
|
+
--color-base-200: oklch(93% 0 0);
|
293
|
+
--color-base-300: oklch(86% 0 0);
|
294
|
+
--color-base-content: oklch(35.519% 0.032 262.988);
|
295
|
+
--color-primary: oklch(76.662% 0.135 153.45);
|
296
|
+
--color-primary-content: oklch(33.387% 0.04 162.24);
|
297
|
+
--color-secondary: oklch(61.302% 0.202 261.294);
|
298
|
+
--color-secondary-content: oklch(100% 0 0);
|
299
|
+
--color-accent: oklch(72.772% 0.149 33.2);
|
300
|
+
--color-accent-content: oklch(0% 0 0);
|
301
|
+
--color-neutral: oklch(35.519% 0.032 262.988);
|
302
|
+
--color-neutral-content: oklch(98.462% 0.001 247.838);
|
303
|
+
--color-info: oklch(72.06% 0.191 231.6);
|
304
|
+
--color-info-content: oklch(0% 0 0);
|
305
|
+
--color-success: oklch(64.8% 0.15 160);
|
306
|
+
--color-success-content: oklch(0% 0 0);
|
307
|
+
--color-warning: oklch(84.71% 0.199 83.87);
|
308
|
+
--color-warning-content: oklch(0% 0 0);
|
309
|
+
--color-error: oklch(71.76% 0.221 22.18);
|
310
|
+
--color-error-content: oklch(0% 0 0);
|
311
|
+
--radius-selector: 1rem;
|
312
|
+
--radius-field: 0.5rem;
|
313
|
+
--radius-box: 1rem;
|
314
|
+
--size-selector: 0.25rem;
|
315
|
+
--size-field: 0.25rem;
|
316
|
+
--border: 1px;
|
317
|
+
--depth: 1;
|
318
|
+
--noise: 1;
|
319
|
+
}"""
|
320
|
+
|
321
|
+
return {"themeInput": emerald_theme, "themeConfig": None, "generatedCSS": ""}
|
322
|
+
|
323
|
+
def example_payload(self) -> Any:
|
324
|
+
return {
|
325
|
+
"themeInput": "sample theme",
|
326
|
+
"generatedCSS": ":root { --color-primary: blue; }",
|
327
|
+
}
|
328
|
+
|
329
|
+
def example_value(self) -> Any:
|
330
|
+
return {
|
331
|
+
"themeInput": "sample theme",
|
332
|
+
"generatedCSS": ":root { --color-primary: blue; }",
|
333
|
+
}
|
334
|
+
|
335
|
+
def api_info(self) -> dict[str, Any]:
|
336
|
+
return {"type": {}, "description": "Gradio theme configuration object"}
|